Node.js Intermediate: Express, Validation, SQLite and Tests
Middleware, Validation and Errors
In this lesson you will
- Write and order middleware functions
- Validate request bodies with a schema
- Build an error handler that returns safe, helpful responses
Introduction
Almost everything in Express is middleware: functions that run between the request arriving and the response leaving. Good APIs use middleware for logging, parsing and security, and they never trust incoming data. They also deal with errors in one consistent place.
What middleware is
A middleware function receives req, res and next. It can change the request, end the response, or call next() to pass control to the next function.
You can also add middleware to a single route. Order matters: middleware and routes run in the order defined. If you forget next(), the request hangs.
Common middleware: express.json() to parse bodies, logging, authentication checks, rate limiting and CORS headers.
Quick quiz
What happens if a middleware neither sends a response nor calls next()?
Never trust input
Anything from a client, such as a body, a query string, a parameter or a header, can be wrong or hostile. Validate it before using it. A schema library such as Zod describes the shape you expect:
Sending {"title": ""} returns, in a real run:
400 {
error: 'Invalid input',
issues: [ 'Too small: expected string to have >=1 characters' ]
}Notice that the code uses parsed.data, the validated values, not the raw body. Validation also strips or rejects unexpected data, depending on your schema. Always validate on the server, even if the browser form also checks.
A central error handler
An error handler has four arguments and goes last:
In a real run, sending the broken text {bad to POST /todos returns 400, because the handler recognises the parse failure. Without that line, every bad body would be reported as a 500, blaming your server for the client's mistake.
Key points:
- Log the real error on the server, and send a generic message to the client. Never return stack traces or internal details in production.
- Use 400 for bad input, 401 and 403 for authentication and permission problems, 404 for missing resources and 500 for your own faults.
- Return errors in a consistent shape, such as
{ "error": "..." }. - Express 5 passes errors from async handlers to this function automatically.
Putting it together
A typical order of an app:
- Security and logging middleware
- Body parsers
- Routes (each validating its input)
- A fallback for unknown paths returning 404
- The error handler
Try it yourself
Add validation to PATCH /todos/:id that accepts an optional title and an optional boolean done, but rejects empty objects. Add a 404 fallback middleware for unknown routes, and test each case.
Summary
- Middleware runs in order and must either respond or call next().
- Validate every input with a schema, and use only the validated data.
- Handle errors in one place, log details privately and return safe, consistent responses.
Lesson complete
+20 XP
Nice work. Keep going while it is fresh.
Sources and further reading