Node and Express are the runtime and HTTP framework for your API. Topics here cover bootstrapping the app, parsing JSON bodies, and structured logging—everything that runs before a route handler executes. Master this layer first so later routers and controllers plug into a consistent foundation.
Node / Express
Node and Express are the runtime and HTTP framework for your API. Topics here cover bootstrapping the app, parsing JSON bodies, and structured logging—everything that runs before a route handler executes. Master this layer first so later routers and controllers plug into a consistent foundation.
Select a function type from the sidebar, or browse topics below.
Create the Express app instance and register core middleware before defining routes.
Learn: Every Express API starts with a single app instance. Register global middleware (JSON parsing, logging, CORS) before routes so every request is prepared the same way. Keep app.js focused on wiring; move route logic into routers as the project grows.
A Promise represents async work that will finish later (database, file, network). Knex queries return promises; Express handlers use async/await to read results cleanly.
Learn: Before async/await, Promises were the standard way to handle async results in Node. Knex still returns a Promise from every query builder chain. Understanding then/catch helps you read older examples; writing new code with async/await is usually clearer in controllers and services.
Register pino-http (or similar) on the app to log each incoming request.
Learn: Request logging answers “what hit the server?” in production. pino-http attaches a logger to each request so handlers can log with context. Place it early in the middleware stack, after body parsers if you log POST bodies sparingly.
Mark controller functions async and await service/Knex calls. Always forward failures with next(err) so the error middleware can respond.
Learn: Async handlers must forward rejections with `next(err)`—otherwise Express may not invoke your error middleware. Wrapping async functions or using a small `asyncHandler` utility removes repetitive try/catch blocks.
Centralize logger setup in a config module with log level and custom request IDs.
Learn: Centralizing logger setup avoids duplicating log level and formatting rules. Use environment variables for LOG_LEVEL in development vs production, and generate a request ID (e.g. nanoid) so log lines for one request can be correlated.
Apply the cors middleware on the Express app so all routes allow cross-origin requests.
Learn: Browsers block cross-origin API calls unless the server sends CORS headers. App-wide CORS is simplest when a single frontend origin talks to your API. Tighten options in production—avoid `origin: '*'` if cookies or credentials matter.
Read configuration from process.env (often with dotenv) for ports, secrets, and log levels.
Learn: Twelve-factor apps store config in the environment: ports, database URLs, API keys, and log levels. Never commit secrets. Use `.env` locally with dotenv; in production the host injects variables.
Service functions return promises (or async functions that return data). Controllers await them; do not mix callback-style Knex with async/await in the same path.
Learn: Keep Promise-returning database code in services. The controller awaits one or more service calls and maps results to HTTP. If you return knex(...) directly from a service without await inside, the controller still awaits the same Promise—both styles work if errors propagate to next(err).
Supplementary examples
async service function
async function remove(pasteId) {
const deleted = await knex("pastes").where({ paste_id: pasteId }).del();
if (!deleted) {
const err = new Error("Paste not found");
err.status = 404;
throw err;
}
}
Course example
// service — return the Knex promise (or use async/await inside)
function listByUser(userId) {
return knex("pastes")
.where({ user_id: userId })
.orderBy("created_at", "desc");
}
// controller
async function list(req, res, next) {
try {
const rows = await service.listByUser(req.params.userId);
res.json({ data: rows });
} catch (err) {
next(err);
}
}