The architecture of Fastify, x-rayed
Fastify's lib/ directory is a study in single-responsibility: 32 files, 32 modules, essentially one concern per file — route.js, reply.js, decorate.js, hooks.js. The x-ray shows a framework that pushed its architecture into its ecosystem: the core stays small because everything else is a plugin.
archsteer xray v0.4.1 against fastify/fastify@94bcbcc (lib/) on 2026-07-05. Read-only static analysis — no code executed, no architecture rules declared. Structure, not judgment.Module dependency graph
Top 14 modules by connectivity; an arrow means "imports from".
graph LR
m0["content-type-parser"]
m1["error-handler"]
m2["errors"]
m3["four-oh-four"]
m4["handle-request"]
m5["hooks"]
m6["logger-factory"]
m7["plugin-override"]
m8["reply"]
m9["request"]
m10["route"]
m11["schemas"]
m12["server"]
m13["symbols"]
m0 --> m2
m0 --> m13
m1 --> m2
m1 --> m11
m1 --> m13
m3 --> m1
m3 --> m2
m3 --> m5
m3 --> m6
m3 --> m8
m3 --> m9
m3 --> m13
m4 --> m2
m4 --> m5
m4 --> m13
m5 --> m2
m5 --> m13
m6 --> m2
m6 --> m13
m7 --> m0
m7 --> m5
m7 --> m8
m7 --> m9
m7 --> m13
m8 --> m1
m8 --> m2
m8 --> m4
m8 --> m5
m8 --> m6
m8 --> m11
m8 --> m13
m9 --> m2
m9 --> m13
m10 --> m1
m10 --> m2
m10 --> m4
m10 --> m5
m10 --> m6
m10 --> m11
m10 --> m13
m11 --> m2
m11 --> m13
m12 --> m2
m12 --> m5
m12 --> m13Components by module
Most connected components
Top 20 of 32 by exported API surface and dependency count.
| Component | Module | Key exports |
|---|---|---|
route.js | route | default |
logger-factory.js | logger-factory | default |
server.js | server | default |
reply.js | reply | default |
four-oh-four.js | four-oh-four | default |
handle-request.js | handle-request | default |
content-type-parser.js | content-type-parser | default |
error-handler.js | error-handler | default |
plugin-override.js | plugin-override | default |
request.js | request | default |
plugin-utils.js | plugin-utils | default |
schemas.js | schemas | default |
validation.js | validation | default |
log-controller.js | log-controller | default |
schema-controller.js | schema-controller | default |
wrap-thenable.js | wrap-thenable | default |
initial-config-validation.js | initial-config-validation | default |
hooks.js | hooks | default |
decorate.js | decorate | default |
logger-pino.js | logger-pino | default |
Declared runtime dependencies
What the structure teaches
One file, one concern, no exceptions
Every component in lib/ maps to exactly one framework capability: content-type parsing, error serialization, request decoration, hook execution. The 1:1 file-to-module ratio in the x-ray isn't an accident of counting — it's the codebase's actual organizing principle.
The @fastify/ scope is the architecture
Most of the 15 runtime dependencies are Fastify's own packages — ajv-compiler, fast-json-stringify-compiler, error, proxy-addr. The framework factored its subsystems into independently-versioned modules under one npm scope: the monorepo pattern without the monorepo.
Speed is a structural choice
route.js and reply.js — the hottest request-path components — are the most connected in the graph. Fastify compiles JSON serialization (fast-json-stringify) and validation (ajv) ahead of time per route, which is why the routing layer, not a middleware chain, is the center of the architecture.
X-ray your own repo
This page is unedited archsteer xray output plus commentary. The same map of your codebase takes one command, runs locally, and never sends code anywhere: pip install archsteer && archsteer xray
FAQ
How is Fastify structured internally?
As ~32 single-purpose modules in lib/ — route registration, reply handling, hooks, decorators, content-type parsing, error handling — coordinated by fastify.js as the composition root. Cross-cutting subsystems (schema compilation, JSON serialization) live in separate @fastify/-scoped packages among its 15 runtime dependencies.
What makes Fastify fast architecturally?
Ahead-of-time compilation per route: JSON schemas compile to specialized validation functions (ajv) and serializers (fast-json-stringify) at registration time, not per-request. Plus an encapsulated plugin tree instead of a linear middleware chain, so requests only pay for what their route actually registered.
How was this architecture map generated?
By running `archsteer xray` (an open-source, MIT-licensed CLI) against the lib/ directory of the public GitHub repo — read-only static analysis, no code executed. Reproduce it with `pip install archsteer && archsteer xray`.
More x-rays
The architecture of FastAPI, x-rayed
An auto-generated architecture map of FastAPI: 48 components across 28 modules, the module…
The architecture of Flask, x-rayed
An auto-generated architecture map of Flask: 24 components, the sansio core split, bluepri…
The architecture of HTTPX, x-rayed
An auto-generated architecture map of HTTPX: 23 underscore-private components, the pluggab…
The architecture of Starlette, x-rayed
An auto-generated architecture map of Starlette: 34 components, the middleware stack, and …