Well-structured backends separate concerns: routers define URLs, controllers translate HTTP to application calls, services own data access, and middleware handles cross-cutting behavior (validation, 404, errors). Study these patterns to keep files small, testable, and aligned with REST conventions.
Building routes
Matches the lesson Building routes: define paths and HTTP verbs on a router or app.
Instantiate Router(), wire handlers, and export the router for mounting on the app.
Learn: An Express Router is a mini-app: it has its own middleware stack and routes, then mounts on the main app. `mergeParams: true` lets nested routers see parent params (e.g. `:userId` from a parent mount). Always export the router for `app.use`.
Attach handlers with app.get/post before splitting logic into router modules.
Learn: Defining routes directly on `app` is fine for tiny projects or quick experiments. As endpoints multiply, inline handlers become hard to test and navigate—splitting into router modules keeps each file responsible for one resource area.
// New middleware function to validate the request body
function bodyHasTextProperty(req, res, next) {
const { data: { text } = {} } = req.body;
if (text) {
return next(); // Call `next()` without an error message if the result exists
}
next("A 'text' property is required.");
}
let lastPasteId = pastes.reduce((maxId, paste) => Math.max(maxId, paste.id), 0);
app.post(
"/pastes",
bodyHasTextProperty, // Add validation middleware function
(req, res) => {
// Route handler no longer has validation code.
const { data: { name, syntax, exposure, expiration, text, user_id } = {} } = req.body;
const newPaste = {
id: ++lastPasteId, // Increment last id then assign as the current ID
name,
syntax,
exposure,
expiration,
text,
user_id,
};
pastes.push(newPaste);
res.status(201).json({ data: newPaste });
}
);
Map GET, POST, PUT, and DELETE handlers to paths using router methods or router.route().
Learn: REST maps actions to HTTP verbs: GET reads, POST creates, PUT/PATCH updates, DELETE removes. Use `router.route(path)` to chain verbs on the same path. Handlers should delegate to controllers, not contain SQL.
Use app.use('/prefix', router) to group related endpoints under a base path.
Learn: Mounting groups related endpoints under a prefix (`/users/:userId/pastes`) keeps URLs readable and mirrors resource ownership. Order matters: register specific routes before catch-all middleware like notFound.