1. Courses
  2. Developer Foundations
  3. JavaScript in the Real World
  4. Stop 4

40 minIntermediate

From script to service: a small API in Node

A script runs once and exits. A service listens all day, serves many people at once, checks every request, logs what happened and fails politely. This lesson builds a small stock API three ways, then properly in NestJS, the way Mangrove's partner-api is built, with validation, config, errors and an end-to-end test.

By the end you can

  • Explain what an HTTP server in Node does, and what Express and NestJS add on top
  • Name the parts of a NestJS app (modules, controllers, services, DTOs, pipes, guards) and the order a request passes through them
  • Load and validate configuration from .env, and keep secrets out of Git
  • Return the right error status for each failure, and log in a format a log server can read
  • Write an end-to-end test with supertest, and run the API in watch mode

The everyday picture

Cooking biryani at home for your family is a script: one cook, one meal, done. Turning that kitchen into a small restaurant on a busy street changes everything around the cooking. Now there is a menu (which dishes exist, and what you must say to order one), a counter that takes orders and hands out food, cooks who only cook, a rule book (no outside food, pay first, staff only in the kitchen) and a daily log of what was sold and what went wrong.

A web service has the same parts. Routes are the menu. The controller is the counter: it takes a request and gives back a response, and it does not cook. The service is the kitchen, where the business rules live. Guards and pipes are the rule book, checked at the door. Logs are the daily log. NestJS gives each of these a fixed place, so a new developer can find any of them in any project.

An HTTP server is a function that answers requests

At its core, Node's node:http module calls one function for every request with two objects: req (method, URL, headers, body) and res (where you write the status, headers and body). Everything else is built on that. Here is a stock lookup with nothing but Node:

JavaScript
// server.js: an HTTP API with nothing but Node
import { createServer } from 'node:http';

const stock = new Map([['D027:A1001', 42], ['D027:A2040', 3]]);

const server = createServer((req, res) => {
  const send = (status, body) => {
    res.writeHead(status, { 'content-type': 'application/json' });
    res.end(JSON.stringify(body));
  };
  const match = req.url.match(/^\/stock\/([A-Z]\d{3})\/(A\d{4})$/);   // routing by hand
  if (req.method === 'GET' && match) {
    const qty = stock.get(`${match[1]}:${match[2]}`);
    return qty === undefined ? send(404, { error: 'not found' }) : send(200, { qty });
  }
  send(404, { error: 'no such route' });
});

server.listen(3001, () => console.log('listening on http://localhost:3001'));
process.on('SIGINT', () => server.close(() => process.exit(0)));       // Ctrl+C: finish, then exit

It works, and it shows what you would have to build yourself for a real API: routing with regular expressions, reading and parsing JSON bodies, validation, error responses, logging, auth, CORS, tests. Because Node runs your handlers on one thread (The event loop), one slow synchronous loop here blocks every store's request at once. That stays true whatever framework you add.

Express is the classic small layer on top. It adds a router with named parameters (/stock/:siteId/:articleCode) and : functions that run in order on every request, such as express.json() to parse bodies.

JavaScript
// express-server.js: the same endpoint with Express 5
import express from 'express';

const stock = new Map([['D027:A1001', 42], ['D027:A2040', 3]]);
const app = express();
app.use(express.json());                                 // middleware: parse JSON bodies

app.get('/stock/:siteId/:articleCode', (req, res) => {  // routing with named params
  const { siteId, articleCode } = req.params;
  const qty = stock.get(`${siteId}:${articleCode}`);
  if (qty === undefined) return res.status(404).json({ error: 'not found' });
  res.json({ siteId, articleCode, qty });
});

app.listen(3002, () => console.log('listening on http://localhost:3002'));

Express leaves structure up to you, so two Express projects can look completely different. NestJS runs on top of Express by default and adds that structure: modules, classes with decorators, , and fixed places for validation, auth and error handling. That is why teams with several developers and many endpoints, like a POS backend, often choose it.

Quick check

One route handler runs a synchronous loop that takes 2 seconds. Ten tills call other, fast routes during those 2 seconds. What happens to them?

Loading the rest of the lesson…