Study Notes · Sep 2026

Node.js, Express, and REST API Foundations

  • Node.js
  • Express
  • JavaScript
  • REST API
  • Middleware
  • JWT
  • npm
  • Async
Node.js, Express, and REST API Foundations cover image

What "back end" means

Back-end developers write the code that connects the browser to servers, databases, and business logic. A typical back end is built from several pieces working together:

  • Database servers — house, retrieve, and deliver data
  • Application servers — sit between the database and the web server, running business logic and turning raw data into content
  • Web servers — serve content and route requests to clients
  • Web APIs — define how two pieces of software talk to each other (a web service is a Web API that uses HTTP)

Node.js is a JavaScript runtime built on Chrome's V8 engine that lets JavaScript run outside the browser, on the server. It's single-threaded but handles I/O through non-blocking events and callbacks — which is exactly why it scales well for concurrent, I/O-heavy servers. It's used in production at Uber, LinkedIn, PayPal, and eBay.

Express is a minimal, configurable framework that runs on top of Node.js, abstracting away its lower-level HTTP APIs with clean routing and middleware.

A back end has to account for load (concurrent users and transactions), scalability (handling more load without degrading), security (authentication, malware prevention), and performance.

Modules, packages, and npm

Every JavaScript file in Node is a module — a self-contained, reusable unit of code. A package is a directory of one or more modules described by a package.json file.

// math.js — expose functions from a module
exports.add = (a, b) => a + b;

// app.js — consume it
const math = require('./math');
math.add(2, 3); // 5

require() and import both load modules, but they aren't the same:

require() (CommonJS)import (ES Modules)
PositionAnywhere in the fileTop of the file only
BindingDynamicStatic
LoadingSynchronousAsynchronous
  • Modules come in three flavors: core (built into Node), local (your own code), and third-party (installed via npm).
  • npm install <package> installs locally by default — only that project can use it. A global install (-g) makes it available machine-wide, which can quietly break version compatibility across projects.

Async programming: callbacks, promises, async/await

Node runs network operations non-blocking: a call returns immediately, and a callback fires once the result is ready — so the server is never stuck waiting.

// error-first callback convention
fs.readFile('data.json', (err, data) => {
  if (err) return console.error(err);
  console.log(data);
});

Deeply nested callbacks ("callback hell") are hard to read, so modern code prefers Promises and async/await:

async function getUser(id) {
  const res = await axios.get(`/api/users/${id}`); // pauses until resolved
  return res.data;
}

A Promise is always in one of three states — pending, fulfilled, or rejected — and async functions always return one, even if you never write new Promise(...) yourself.

  • JSON.parse() turns a JSON string into a JS object; JSON.stringify() does the reverse.
  • Axios is a promise-based HTTP client that wraps this pattern for REST calls, on both the browser and Node.

Express: routing, middleware, and auth

A route pairs an HTTP method and a URL path with a handler function:

const express = require('express');
const app = express();

app.get('/users/:id', (req, res) => {
  res.json({ id: req.params.id, name: 'Ada' });
});

app.listen(3000);

Middleware are functions with access to req, res, and next — they run in a chain, and each one can respond, transform the request, or hand off to the next middleware. Express uses them for parsing JSON bodies, authentication, logging, and error handling.

app.use((req, res, next) => {
  console.log(`${req.method} ${req.path}`);
  next(); // pass control to the next handler
});

Three common authentication strategies:

  • Session-based — credentials are checked once; the server stores a session ID that's echoed back in a cookie on every request.
  • Token-based (JWT) — the client receives a signed token (header, payload, signature) and sends it with each request as proof of identity; scales better than sessions since the server stores nothing.
  • Passwordless — biometrics, magic links, or one-time codes, backed by public/private key encryption.

Designing a REST API

Every HTTP response carries a status code that tells the client what happened:

RangeMeaning
200–299Success
300–399Resource moved
400–499Client error
500–599Server error

The core HTTP methods map cleanly onto CRUD:

GET    /users        # list
GET    /users/123    # read one
POST   /users        # create         -> 201
PUT    /users/123    # update/replace -> 200 or 404
DELETE /users/123    # delete         -> 204

PUT and DELETE are idempotent — calling them repeatedly has the same effect as calling them once; POST is not.

Good REST URLs:

  • Use nouns, not verbs — /users, not /getUsers
  • Use plural nouns for collections — /users/123, not /user/123
  • Use lowercase and hyphens, never camelCase or underscores
  • Never end in a trailing slash

Anti-pattern: GET /getUser/123 and POST /addUser bake the verb into the URL — the RESTful versions are GET /users/123 and POST /users, since the HTTP method already says what's happening.

← Back to Full-Stack City