fastify-xconfig
fastify-xconfig
Opinionated Fastify plugin that bootstraps a complete API server in one register call: CORS, rate limiting, multipart body parsing, back-pressure monitoring, opt-in Bugsnag error tracking and Prisma, fancy error responses, a /health endpoint with system metrics, and utility decorators under a single fastify.xConfig namespace. Register it once at app startup before route plugins.
Installation
npm install @xenterprises/fastify-xconfig fastify@5
# if using Prisma:
npm install @prisma/client
Quick Start
Bare registration works — Prisma and Bugsnag are opt-in and simply stay disabled when their options are omitted:
import Fastify from "fastify";
import xConfig from "@xenterprises/fastify-xconfig";
const fastify = Fastify({ logger: true });
await fastify.register(xConfig, {
environment: process.env.NODE_ENV, // the plugin never reads env itself
});
fastify.xConfig.slugify("Hello World"); // "hello-world"
fastify.xConfig.randomUUID(); // "a1b2c3d4-..."
fastify.xConfig.formatBytes(1048576); // "1 MB"
await fastify.listen({ port: 3000 });
A full registration with integrations and middleware tuned:
import { PrismaClient } from "@prisma/client";
await fastify.register(xConfig, {
environment: process.env.NODE_ENV,
prisma: { client: PrismaClient }, // enables fastify.prisma
bugsnag: { apiKey: process.env.BUGSNAG_API_KEY }, // enables fastify.bugsnag
cors: { origin: ["https://app.example.com"], credentials: true },
rateLimit: { max: 100, timeWindow: "1 minute" },
multipart: { limits: { fileSize: 52428800 } },
fancyErrors: true,
});
Options
All configuration is passed at registration. The plugin never reads process.env — the consumer owns env access and passes values in.
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
environment | string | "development" | No | Drives stack traces in error responses and the /health payload |
professional | boolean | false | No | Suppress route listing on startup when true |
fancyErrors | boolean | true | No | Enable formatted error responses with status codes |
prisma | object | {} (disabled) | No | Prisma integration config (see below) |
bugsnag | object | {} (disabled) | No | Bugsnag error tracking config (see below) |
cors | object | localhost origins | No | CORS config forwarded to @fastify/cors |
rateLimit | object | @fastify/rate-limit defaults | No | Rate limiting config forwarded to @fastify/rate-limit |
multipart | object | @fastify/multipart defaults | No | Multipart config forwarded to @fastify/multipart |
underPressure | object | @fastify/under-pressure defaults | No | Back-pressure config forwarded to @fastify/under-pressure |
Every middleware option bag accepts active: false to disable that middleware entirely.
prisma Options
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
client | PrismaClient class or instance | — | No | Omit prisma entirely to leave Prisma disabled. Pass your generated PrismaClient class (instantiated with the remaining options) or an instance you created |
active | boolean | — | No | Only active: true is meaningful: it makes client required, throwing at registration when missing. active: false also disables Prisma |
...rest | object | — | No | Passed directly to new PrismaClient(...) when client is a class |
bugsnag Options
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
apiKey | string | — | No | Omit bugsnag entirely to leave Bugsnag disabled. Provide a key to enable it |
active | boolean | — | No | Only active: true is meaningful: it makes apiKey required, throwing at registration when missing. active: false also disables Bugsnag |
cors Options
| Name | Type | Default | Required | Description |
|---|---|---|---|---|
active | boolean | true | No | Enable or disable CORS |
origin | string|string[] | ["http://localhost:3000", "http://localhost:3001"] | No | Allowed origins — static localhost-only default; deployed apps MUST pass cors.origin explicitly |
credentials | boolean | true | No | Allow credentials |
methods | string[] | ["GET","POST","PUT","DELETE","OPTIONS"] | No | Allowed HTTP methods |
Methods
Utility decorators live under a single fastify.xConfig namespace:
- xConfig.echo() — Returns
"Hello from X Enterprises!" - xConfig.slugify(string) — Converts a string to a URL-safe slug
- xConfig.randomUUID() — Generates a UUID v4 string;
xConfig.generateUUID()is an alias - xConfig.formatBytes(bytes, decimals?) — Formats a byte count to a human-readable string
The plugin also registers @fastify/sensible, so its decorators (reply.notFound(), fastify.httpErrors, …) are available.
Routes registered by the plugin:
- GET /health — System health check with memory, CPU, disk, and dependency metrics
fastify.prisma
When Prisma is enabled (prisma.client provided), fastify.prisma holds the connected PrismaClient instance. It is disconnected automatically via an onClose hook.
const user = await fastify.prisma.user.findUnique({ where: { id: userId } });
Error Reference
Registration fails fast. Every error is an Error whose message names the plugin, the option, and a correct usage example:
| Error message | Cause |
|---|---|
xconfig: option `environment` must be a string, e.g. `app.register(xConfig, { environment: "production" })` | environment option is not a string |
xconfig: option `professional` must be a boolean, e.g. `app.register(xConfig, { professional: true })` | professional option is not a boolean |
xconfig: option `fancyErrors` must be a boolean, e.g. `app.register(xConfig, { fancyErrors: false })` | fancyErrors option is not a boolean |
xconfig: option `prisma.client` is required when `prisma.active` is true — pass your generated PrismaClient class or an instance, e.g. `app.register(xConfig, { prisma: { client: new PrismaClient() } })` — or omit `prisma` to leave it disabled | prisma.active: true but no prisma.client provided |
xconfig: option `prisma.client` must be a PrismaClient class or instance | prisma.client is neither a class nor an instance |
xconfig: option `bugsnag.apiKey` is required when `bugsnag.active` is true, e.g. `app.register(xConfig, { bugsnag: { apiKey: '...' } })` — or omit `bugsnag` to leave it disabled | bugsnag.active: true but no bugsnag.apiKey provided |
xconfig: option `bugsnag.apiKey` must be a string | bugsnag.apiKey is present but not a string |
At runtime, with fancyErrors enabled (default), route errors are returned as { status, message, stack? }; stack is included only when environment is not "production". With fancyErrors: false, Fastify's default error handler is used.
Environment Variables
The plugin never reads process.env. The consumer sets env vars and passes the values into app.register(xConfig, { ... }) — none of these are inputs the plugin reads itself:
| Variable | Consumer passes it via | Description |
|---|---|---|
NODE_ENV | environment option | "production" disables error stack traces in fancy error responses |
CORS_ORIGIN | cors.origin | Allowed origins (default is static localhost-only, no env fallback) |
RATE_LIMIT_MAX | rateLimit.max | Max requests per window |
RATE_LIMIT_TIME_WINDOW | rateLimit.timeWindow | Rate limit time window |
BUGSNAG_API_KEY | bugsnag.apiKey | Bugsnag project API key (enables the integration) |
DATABASE_URL | your own PrismaClient constructor | Database connection string — read by Prisma, never by this plugin |
How It Works
xConfig is a fastify-plugin-wrapped orchestrator that registers sub-plugins in dependency order: Prisma (opt-in; decorates fastify.prisma + onClose disconnect), CORS/under-pressure/rate-limit/multipart middleware (each skippable via active: false), opt-in Bugsnag integration (decorates fastify.bugsnag), a custom errorHandler for fancy error formatting, @fastify/sensible HTTP utilities, the fastify.xConfig utility namespace (echo, slugify, generateUUID, randomUUID, formatBytes), a GET /health route with memory/CPU/disk and database health checks, and finally a lifecycle hook that logs all registered routes on startup unless professional: true. All decorators and routes are hoisted to the parent scope via fastify-plugin.
AI Context
package: "@xenterprises/fastify-xconfig"
type: fastify-plugin
use-when: Bootstrap a Fastify app with CORS, rate limiting, multipart, opt-in Prisma/Bugsnag, health check, and utility decorators in one registration
decorators: fastify.xConfig (echo, slugify, generateUUID, randomUUID, formatBytes), fastify.prisma (opt-in), fastify.bugsnag (opt-in)
routes: GET /health (memory, CPU, disk, DB metrics)
env: plugin never reads process.env — consumer passes values via options (environment, cors.origin, rateLimit.*, bugsnag.apiKey, prisma.client)
register-before: route plugins — xConfig must be registered first
