Phase 5 · Node.js & Full StackModule 33~50 min read

HTTP Servers & REST APIs

Design and implement maintainable server endpoints with routing, middleware, validation, and errors.

What you'll learn

A maintainable API separates transport, routing, validation, domain behavior, and error translation. REST gives resources predictable methods, representations, and status codes.

By the end of this lesson, you'll be able to:

  • Design resource-oriented endpoints
  • Build middleware boundaries
  • Return consistent errors and pagination

Core mental model

Use this decision table as a compact reference. Focus on what each tool means and when it earns its place in production code.

ConceptWhat it meansDecision rule
RouteMethod and path mapped to one use caseName resources with nouns and use HTTP semantics
MiddlewareOrdered request/response cross-cutting stepReserve for shared concerns, not hidden domain logic
Problem responseStable machine-readable error shapeReturn status, code, message, and safe details

Professional workflow

Build the behavior in small, observable steps. Each step should leave something you can inspect or test.

  1. Describe the HTTP API boundary: inputs, outputs, state, timing, and expected failures.
  2. Implement the smallest correct path with names that expose intent.
  3. Add edge cases and failure handling before introducing abstractions.
  4. Verify behavior with realistic data and one deliberately adversarial example.
  5. Refactor only after the observable behavior is protected.

Make behavior observable

Before optimizing or abstracting, make inputs, outputs, state changes, timing, and failure paths visible. JavaScript becomes much easier to reason about when hidden work is exposed.

Guided code lab

Build a small Node JSON route

Transport concerns remain visible: method, URL, status, headers, and body.

server.js
import { createServer } from "node:http";
const server = createServer((request, response) => {
  if (request.method === "GET" && request.url === "/api/courses") {
    response.writeHead(200, { "Content-Type": "application/json" });
    response.end(JSON.stringify([{ id: "javascript", title: "JavaScript" }]));
    return;
  }
  response.writeHead(404, { "Content-Type": "application/json" });
  response.end(JSON.stringify({ code: "not_found", message: "Route not found" }));
});
server.listen(3000);

Validate at the route boundary

The transport layer translates input into a trusted command before invoking domain behavior.

create-course.js
function parseCourse(body) {
  if (!body || typeof body.title !== "string" || body.title.trim() === "") {
    throw new TypeError("title is required");
  }
  return { title: body.title.trim(), published: Boolean(body.published) };
}

Production practice

Contract

Make the HTTP API boundary explicit with validated inputs, structured outputs, owned resources, and stable failures.

Verification

Exercise normal work, invalid input, dependency failure, concurrency, and graceful cleanup in automated tests.

Operations

Use structured logs, health signals, timeouts, and configuration that can change without editing source code.

Common failure mode

Returning 200 for every outcome forces clients to reverse-engineer success from arbitrary response bodies.

Independent workshop

Build a course-notes REST API with clean transport and domain layers.

Your finished workshop must include:

  • CRUD resource routes
  • Validated bodies and consistent errors
  • Cursor or page pagination with integration tests

Definition of done

Demonstrate the happy path and at least two edge cases, keep responsibilities separated, and add a short note explaining one design choice.

Recap & quick check

Key takeaways

  • Routes map transport to use cases
  • Validation belongs at boundaries
  • Status codes carry semantics
  • Consistent errors simplify clients

Quick check

1. Which method normally creates a subordinate resource?

2. Where should request validation occur?

3. What status fits a missing resource?

Keep the workshop. Later modules deliberately build on these decisions, so each exercise can become part of your final portfolio architecture.