Skip to solution
hardSystem Design

How do you secure your Node.js API endpoints?

384 views
01

Understand the problem

Question presented to candidate: "An API endpoint accepts a JSON body and returns data from a database. Walk through every layer of security that should exist between the raw incoming request and your business logic actually running."

What a strong answer should cover:

  • Securing an API endpoint is a layered problem, not one control: authentication (who is making this request), authorization (are they allowed to do this specific thing), input validation (is the request body/params/query actually well-formed and safe), rate limiting (is this client making requests at an acceptable rate), and transport/header-level protections (HTTPS, security headers) — each addressing a genuinely different failure mode.
  • Authentication commonly uses a JWT or session-based token, verified on every request — the dedicated JWT-vs-session-based-authentication question covers the specific trade-offs between the two approaches and where refresh tokens fit.
  • Input validation with a schema library (zod/joi, covered in its own dedicated question) should run before any business logic touches the request body — rejecting a malformed or unexpected request shape immediately, rather than letting invalid data reach deeper code paths where it could enable SQL injection, prototype pollution, or other injection classes (each covered with a real demonstrated exploit in their own dedicated questions).
  • 📌 Verified, not assumed: HTTP security headers via helmet() add real, checkable protection with a single middleware call — confirmed directly: a server without it exposed only x-powered-by: Express; with it, a full protective header set (CSP, HSTS, X-Frame-Options, and others) appeared automatically.
  • Rate limiting (covered fully in its own dedicated question) protects against both brute-force credential attacks and basic denial-of-service, and should apply per-identity (per API key/user, not just per IP) where the endpoint is authenticated, since a single IP is not a reliable proxy for a single real client at scale.
  • CORS and CSRF (both covered in their own dedicated questions, with real tested browser behavior for CSRF specifically) address genuinely different threats from the layers above — a complete answer names them as distinct, not folds them into "authentication" vaguely.
  • A precise answer treats endpoint security as this specific ordered pipeline — headers/transport, rate limiting, authentication, authorization, input validation, then business logic — rather than a flat, unordered list of good ideas.

Clarifying questions expected:

  • "Is this endpoint authenticated at all, or intentionally public?" — decides how much of the pipeline (auth, per-identity rate limiting) actually applies.
  • "Is the concern a specific layer (auth, input validation) or the complete pipeline end to end?"

Code / implementation expected: Yes — the real, verified helmet() header difference grounds the headers layer concretely; the rest of the pipeline cross-links to its own dedicated, individually-verified question.

securityapiauthenticationauthorization
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js API-security system-design interviews — assumes familiarity with the individual topics this answer cross-links to. Difficulty: Hard

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview term:. The

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

A real header comparison confirming helmet's protective effect on an Express endpoint
const express = require("express");
const helmet = require("helmet");

const withoutHelmet = express();
withoutHelmet.get("/", (req, res) => res.json({ ok: true }));

const withHelmet = express();
withHelmet.use(helmet());
withHelmet.get("/", (req, res) => res.json({ ok: true }));

// WITHOUT helmet: [["x-powered-by","Express"]]
// WITH helmet:    [["content-security-policy","..."], ["strict-transport-security","..."],
//                  ["x-content-type-options","nosniff"], ["x-frame-options","SAMEORIGIN"], ...]
// -- a single middleware call, a real, checkable difference
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 143 of 152 decoded in the Node.js track. One more won't hurt.

Back to track