users resource using MySQL and the callback pattern with db.query(), organized with MVC.
CRUD to HTTP Mapping
MVC Folder Structure
Database Table
config/db.js
controllers/userController.js
Each export function handles one CRUD operation usingdb.query() with callbacks.
routes/userRoutes.js
app.js
The Callback Pattern Explained
Everydb.query() call follows the same structure:
The
return keyword before each res.json() is important. It stops the function from continuing after the response is sent. Without it, Express may try to send multiple responses and crash with “Cannot set headers after they are sent.”db.query() Results Reference
Postman Testing Steps
1
Create user
POST /api/users with JSON body: name, email, password. Expect 201 + id.
2
Get all users
GET /api/users with Authorization: Bearer token. Expect 200 + array.
3
Get one user
GET /api/users/1. Expect 200 + user object.
4
Update user
PUT /api/users/1 with updated fields. Expect 200.
5
Delete user
DELETE /api/users/1. Expect 204 No Content.
6
Confirm deletion
GET /api/users/1 again. Expect 404 Not Found.
Common Mistakes
Forgetting return before res.json() inside callbacks
Forgetting return before res.json() inside callbacks
Without
return, code continues running after sending a response. Node.js throws “Cannot set headers after they are sent to the client.” Always add return before every res.status(...).Not checking results.length on SELECT
Not checking results.length on SELECT
db.query() for SELECT always succeeds even if no rows match. Check
results.length === 0 to return a 404.Not checking affectedRows on UPDATE/DELETE
Not checking affectedRows on UPDATE/DELETE
If the ID does not exist,
affectedRows is 0 but err is null. Without the affectedRows check you silently return 200.Nesting too many callbacks
Nesting too many callbacks
Deep nesting (callback inside callback inside callback) is hard to read. For complex flows, consider using named functions instead of anonymous inline callbacks to keep nesting shallow.