Build a small service with Express 5
Express is a Node web framework that maps HTTP requests to functions. It provides routing, response helpers and middleware composition. It does not automatically create a database, authenticate users or validate all incoming data. This course uses Express 5; keep that major version consistent when following the examples.
Start the application
mkdir express-practice
cd express-practice
npm init -y
npm install express@5
Create app.mjs with the following code and run node app.mjs. The .mjs extension enables ES modules without additional package configuration.
import express from 'express';
const app = express();
app.disable('x-powered-by');
app.use(express.json({ limit: '16kb' }));
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
app.post('/api/echo', (req, res) => {
const message = req.body?.message;
if (typeof message !== 'string' || !message.trim() || message.length > 200) {
return res.status(400).json({ error: 'message must contain 1–200 characters' });
}
return res.json({ message: message.trim() });
});
app.use((req, res) => {
res.status(404).json({ error: 'Route not found' });
});
app.use((err, req, res, next) => {
if (res.headersSent) return next(err);
const status = err.status === 413 ? 413 : err.type === 'entity.parse.failed' ? 400 : 500;
res.status(status).json({ error: status === 500 ? 'Internal server error' : 'Invalid request body' });
});
const server = app.listen(3000, '127.0.0.1', () => console.log('http://127.0.0.1:3000'));
server.on('error', error => console.error('Listen failed:', error.code));
Follow a request through the app
express.json runs before the routes and parses JSON requests with a bounded body size. POST /api/echo checks a particular field rather than trusting the entire body. Sending {"message":" hello "} with Content-Type: application/json returns {"message":"hello"}. An empty or non-string message returns 400. GET /health returns a health response; an unmatched path reaches the final 404 middleware.
The four-argument error handler comes last. In Express 5, an async route's returned rejected promise reaches error handling automatically. Detached timers and promises that are not returned still need their own error forwarding. A 404 is an unmatched route, not automatically an exception.
Project boundaries
As the service grows, export the app from one file and start listening in another. Group routes with express.Router and move business rules into service functions. Keep secrets in deployment configuration, validate input at the server boundary, and avoid sending raw stack traces to visitors.
Practice checkpoint
Add GET /api/course returning a fixed course name and lesson count. Confirm that POST /api/echo rejects a numeric message and malformed JSON. Explain why placing the 404 handler before your routes would hide them. Record the request method, URL, status and body for each check.