One exit for errors
Do not format errors inside every route. Send failed requests to one error-handling middleware, then shape the response there.
In Express, an error handler has four arguments: err, req, res, next. Put it after your routes.
Why this matters
A recruiter will test your API with wrong IDs, missing fields, bad JSON, and broken requests. If each route returns errors differently, your project feels unfinished.
A central handler makes your API predictable. Your success responses stay in routes. Your failure responses stay in one place.
Route to handler
In Express 5, async route handlers that reject or throw are passed to error middleware. You do not need the old Express 4 wrapper pattern for every async route.
Use next(error) when you create an error yourself, such as when a resource is not found.
import express from 'express';
const app = express();
app.use(express.json());
const students = [
{ id: '1', name: 'Asha', college: 'VIT' }
];
app.get('/students/:id', async (req, res, next) => {
const student = students.find((item) => item.id === req.params.id);
if (!student) {
const error = new Error('Student not found');
error.status = 404;
return next(error);
}
res.json({ data: student });
});
app.post('/students', async (req, res) => {
if (!req.body?.name) {
const error = new Error('Name is required');
error.status = 400;
throw error;
}
res.status(201).json({ data: req.body });
});
app.use((err, req, res, next) => {
if (res.headersSent) {
return next(err);
}
const status = err.status || err.statusCode || 500;
res.status(status).json({
error: {
message: status === 500 ? 'Internal server error' : err.message,
status
}
});
});
app.listen(process.env.PORT || 3000);Add the handler
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 5 done
Do not leak stacks
Stack traces help you debug, but they should not be shown to users. They can reveal file names, folders, and internal code details.
For your project, return a safe message for 500 errors. Log the full error in your terminal while developing if you need it.
app.use((err, req, res, next) => {
console.error(err);
const status = err.status || err.statusCode || 500;
const message = status === 500 ? 'Internal server error' : err.message;
res.status(status).json({ error: { message, status } });
});Bad JSON is covered
If express.json() cannot parse the request body, it creates a parse error. These errors commonly carry a status like 400.
That means your central handler can also format malformed JSON errors. Test it by sending broken JSON in Postman, Thunder Client, or curl.
curl -X POST http://localhost:3000/students \
-H "Content-Type: application/json" \
-d '{ "name": 'Order matters
If your error handler is above your routes, it will not catch route errors. Express runs middleware in order.
Keep normal middleware first, then routes, then the final error handler.
app.use(express.json());
app.use('/students', studentsRouter);
app.use('/projects', projectsRouter);
app.use((err, req, res, next) => {
// final error response here
});Do this now
Do each one yourself, then tap it to tick it off. The ticks are only a checklist for you: they are not marked or scored.
0 of 5 done