Question presented to candidate: "Your team is starting a new Node.js API. Why would you consider Fastify over Express?"
What a strong answer should cover:
- Express is middleware-first: a chain of
(req, res, next)functions, no built-in request or response validation, JSON serialized by the genericJSON.stringify— whatever the object holds goes out. - Fastify is schema-first: routes declare a JSON Schema for their params, body, and response, and Fastify compiles a fast serializer from that schema ahead of time rather than calling the generic stringifier per request.
- The schema is not just documentation — it does real work: request validation (reject malformed input before your handler runs) and response shaping.
- The precise, worth-knowing behaviour: a response schema strips any field not listed in its
properties, by default — no extra configuration needed. That is the opposite of genericJSON.stringify, and it is a genuine, useful allowlist. - The real gotcha is the other direction: explicitly setting
additionalProperties: trueon a response schema reopens that leak, letting an undeclared field back into the response. That is the setting worth watching for in a schema someone else wrote or generated. - Plugin encapsulation is Fastify's structural answer to Express's global
app.use()— a plugin's decorations and hooks are scoped to it and its children by default, rather than leaking across the whole app. - Express's ecosystem size and familiarity are real advantages, not nothing — a large existing team, and the sheer number of Express-specific middleware packages, are legitimate reasons to stay.
- Frame the choice honestly: Fastify's performance and validation story are real, structural advantages for a new service; migrating an existing large Express app for that reason alone is a much bigger cost-benefit question.
Clarifying questions expected:
- "Is this greenfield or an existing Express codebase?" — changes the calculus completely.
- "Does the team need response-shape guarantees, or just request validation?" — decides how much of the schema story actually matters here.
Code / implementation expected: Optional. The response-schema strip-by-default behaviour is the most convincing thing to actually show.