How to Migrate Express to Fastify for Faster JSON Serialization

Fastify serializes JSON 2-10x faster than Express on data-heavy routes - but only when you declare a response schema. Without it, Fastify falls back to JSON.stringify and the gain nearly disappears. Here's how to write the migration and get the speed.

Backend Engineertypescriptnodejsfastify

Why Express JSON serialization is the bottleneck

Express uses JSON.stringify for every response. For a route that returns an array of 200 customer objects, JSON.stringify reflects over every property on every object at runtime on every request. At low traffic it's invisible; at a few hundred rps it shows up clearly in profiling as the hot path.

Fastify solves this with fast-json-stringify: given a JSON Schema for the response body, it compiles a dedicated serializer at startup that knows exactly which fields to emit and in what order - no reflection, no per-call key walks.

The Express starting point

import express from "express";
const app = express();

app.get("/api/customers", (req, res) => {
  res.json({ customers: CUSTOMERS, count: CUSTOMERS.length });
});

app.listen(8000);

This works, but every call to res.json() invokes JSON.stringify on the full payload.

The Fastify migration

import Fastify from "fastify";
const fastify = Fastify({ logger: false });

fastify.get(
  "/api/customers",
  {
    schema: {
      response: {
        200: {
          type: "object",
          properties: {
            count: { type: "integer" },
            customers: {
              type: "array",
              items: {
                type: "object",
                properties: {
                  id: { type: "integer" },
                  name: { type: "string" },
                  email: { type: "string" },
                  status: { type: "string" },
                  signup_date: { type: "string" },
                },
              },
            },
          },
        },
      },
    },
  },
  async () => ({ customers: CUSTOMERS, count: CUSTOMERS.length })
);

fastify.listen({ port: 8000, host: "0.0.0.0" });

The handler itself barely changes. The difference is the schema.response[200] block passed as the second argument to fastify.get. Fastify reads that schema at startup, compiles a fast-json-stringify serializer, and uses it for every response - not JSON.stringify.

Why the schema placement matters

The schema goes inside the route options object - the second positional argument between the URL string and the handler function:

fastify.get(url, { schema: { response: { 200: { ... } } } }, handler)
//                 ^^^^^^^^ route options ^^^^^^^^

Omitting the schema is the most common migration mistake. Fastify without a response schema is only marginally faster than Express because it still falls through to JSON.stringify for serialization.

Key differences from Express

Want to try it hands-on? HeyDevJob gives you this exact setup in a live cloud workspace in your browser - edit it, run it, and see it work. Free, nothing to install.

Try it in a workspace →

What you'll practice

FAQ

Why is Fastify faster than Express for JSON responses?

Fastify uses fast-json-stringify when you declare a response schema - it compiles a dedicated serializer at startup that skips runtime reflection. Without the schema, Fastify falls back to JSON.stringify and the gain is marginal.

Where does the response schema go in a Fastify route?

In the route options object - the second argument between the URL string and the handler: fastify.get(url, { schema: { response: { 200: { ... } } } }, handler). The schema is keyed by HTTP status code.

Do I need to change the handler when migrating from Express to Fastify?

Mostly no. The main change is making the handler async and returning the value directly instead of calling res.json(). The route logic stays the same; you add the schema options object alongside the handler.

Keep learning

Return the Correct HTTP Status CodeBackend projectRepair Broken API PaginationBackend projectAdd CORS Middleware to an Express APIBackend projectBackend roadmapStep by step to hiredBackend interview questionsSTAR answersAll Backend projectsProjects hub

Learn it by doing. Open this in a live cloud workspace, make the change yourself, and keep a record of the work you can share.

Open the workspace →