Add Student Management mini system

Express + MySQL CRUD API for student records, with a plain HTML frontend.

- Single-file backend (server.js) with 5 CRUD routes
- Proper HTTP status codes and a consistent error envelope
- 8-digit ids: 2-digit year + per-year 6-digit counter (e.g. 26000001),
  allocated under a MySQL advisory lock so concurrent inserts cannot collide
- student_db.sql schema and seed data
- .env for credentials; only .env.example is committed
This commit is contained in:
2026-09-28 18:58:42 +08:00
parent 36ef7f0122
commit 45e4de3557
8 changed files with 1783 additions and 2 deletions
+81 -2
View File
@@ -1,3 +1,82 @@
# bonpin-student-api-mw600
# Student Management System (MW600)
Student Management System mini-system - Express + MySQL CRUD API (MW600)
A mini system for managing student records, built with **Express** and **MySQL**.
Backend is a single file (`server.js`) exposing a CRUD API; the frontend is a
plain HTML page (`index.html`) served from the same server.
## Requirements
- Node.js 20.6+ (uses `process.loadEnvFile`)
- A running MySQL server
## Setup
```sh
npm install
cp .env.example .env # then fill in your MySQL credentials
npm run db:push # creates the database and imports student_db.sql
npm start
```
Open http://localhost:3000
`npm run db:push` creates the `student_db` database and imports the schema and
seed data from `student_db.sql`. It is safe to re-run — the script drops and
recreates the `students` table.
## Database
`student_db.sql` holds the schema and seed rows for a single table:
| Column | Type | Notes |
| ------------ | ------------ | ---------------------------- |
| `id` | `INT` | Primary key, assigned by app |
| `name` | `VARCHAR(100)` | Required |
| `course` | `VARCHAR(50)` | Required |
| `year_level` | `INT` | Required, 1–6 |
### ID format
Ids are **8 digits**: a 2-digit year followed by a 6-digit counter that resets
each year.
```
26 000001 -> first student added in 2026
26 000002 -> second student added in 2026
27 000001 -> first student added in 2027
```
The counter is per year, so ids never collide across years. Because MySQL cannot
generate this pattern, `id` has no `AUTO_INCREMENT` — the application assigns it.
Allocation is guarded by a MySQL advisory lock (`GET_LOCK`) so simultaneous
requests cannot receive the same id.
## API
| Method | Path | Response |
| -------- | -------------------- | -------------------------------------------- |
| `GET` | `/api/students` | `200` list of students |
| `GET` | `/api/students/:id` | `200` student, `400` bad id, `404` not found |
| `POST` | `/api/students` | `201` created, `400` invalid, `415` not JSON |
| `PUT` | `/api/students/:id` | `200` updated, `400` invalid, `404` not found |
| `DELETE` | `/api/students/:id` | `204` deleted, `404` not found |
Errors use one envelope:
```json
{ "error": { "code": "VALIDATION_ERROR", "message": "...", "details": [] } }
```
## Configuration
All connection settings live in `.env` (not committed):
| Variable | Default |
| ------------- | ------------- |
| `DB_HOST` | `127.0.0.1` |
| `DB_PORT` | `3306` |
| `DB_USER` | `root` |
| `DB_PASSWORD` | *required* |
| `DB_NAME` | `student_db` |
| `PORT` | `3000` |