Module 3: Backend Frameworks & REST APIs
Express, Fastify, NestJS & Hono
Writing raw http servers (previous chapter) gets tedious fast — backend frameworks
handle routing, request parsing, and middleware for you:
// Express — the most widely used Node.js backend framework
const express = require("express");
const app = express();
app.use(express.json()); // parse JSON request bodies
app.get("/users", (req, res) => {
res.json([{ id: 1, name: "Chiru" }]);
});
app.listen(3000);
Fastify is a newer, performance-focused alternative to Express with a similar feel. NestJS is a full, opinionated framework (built on Express or Fastify underneath) using TypeScript and classes, closer in structure to something like Spring or .NET, for larger enterprise-style backends. Hono is a very lightweight, modern framework designed to run anywhere — Node, Bun, Cloudflare Workers, and more.
HTTP & REST Fundamentals
REST is a convention for designing APIs around HTTP methods and URLs representing resources:
GET /users → list users
GET /users/5 → get user 5
POST /users → create a new user
PUT /users/5 → replace user 5 entirely
PATCH /users/5 → update part of user 5
DELETE /users/5 → delete user 5
Common status codes: 200 OK, 201 Created, 204 No Content,
400 Bad Request, 401 Unauthorized, 403 Forbidden,
404 Not Found, 500 Internal Server Error.
Middleware & Validation
Middleware is a function that runs between the request arriving and the final handler responding — used for logging, authentication checks, and more:
function logger(req, res, next) {
console.log(`${req.method} ${req.url}`);
next(); // pass control to the next middleware/handler
}
app.use(logger);
// Validation — never trust incoming data as-is
app.post("/users", (req, res) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: "name and email are required" });
}
// proceed to create the user...
});
Authentication: JWT, Sessions & OAuth
// JWT (JSON Web Token) — a signed token proving who a user is, sent with each request
const jwt = require("jsonwebtoken");
let token = jwt.sign({ userId: 5 }, "secret-key", { expiresIn: "1h" });
// client sends this back in a header: Authorization: Bearer <token>
let payload = jwt.verify(token, "secret-key"); // throws if invalid/expired
console.log(payload.userId);
Sessions are the older, server-stored alternative — the server keeps track of logged-in users itself, and the browser holds only a session ID in a cookie. OAuth is the standard for "Sign in with Google/GitHub" style login — a user grants your app permission through the provider, without ever handing your app their password.
CORS, Rate Limiting & Logging
// CORS — allow a frontend on a different origin to call this API (see Module 2's CORS chapter)
const cors = require("cors");
app.use(cors({ origin: "https://mysite.com" }));
// Rate limiting — protect against abuse by capping requests per IP
const rateLimit = require("express-rate-limit");
app.use(rateLimit({ windowMs: 15 * 60 * 1000, max: 100 })); // 100 requests / 15 min
// Logging — record what's happening in production
const morgan = require("morgan");
app.use(morgan("combined"));