A powerful TypeScript library that seamlessly integrates OpenAPI specifications with Fastify routing, providing type-safe API development with automatic validation and comprehensive documentation generation.
$ref supportnpm install openapi-fastify
import Fastify from 'fastify';
import { OpenApiRouter } from 'openapi-fastify';
// Create your OpenAPI specification
const openApiDoc = <const>{
openapi: "3.0.0",
info: {
title: "My API",
version: "1.0.0"
},
components: {
schemas: {
User: {
type: 'object',
properties: {
id: { type: 'number' },
username: { type: 'string' },
email: { type: 'string' }
},
required: ['id', 'username', 'email']
}
}
}
};
// Initialize Fastify and router
const app = Fastify();
const router = new OpenApiRouter(app, openApiDoc, {
autoValidate: {
request: {
validate: true
},
response: {
validate: true
}
}
});
// Simple GET route
router.route("/hello", {
get: router.op(
<const>{
summary: "Say Hello",
responses: {
200: {
description: "A hello world message",
content: {
"application/json": {
schema: {
type: "object",
properties: {
message: { type: "string" }
}
}
}
}
}
}
},
async () => {
return { message: "Hello, world!" };
}
)
});
// POST route with request body validation
router.route("/users", {
post: router.op(
<const>{
summary: "Create a new user",
requestBody: {
required: true,
content: {
"application/json": {
schema: router.ref('#/components/schemas/User')
}
}
},
responses: {
201: {
description: "User created",
content: {
"application/json": {
schema: router.ref('#/components/schemas/User')
}
}
}
}
},
async (request, reply) => {
const user = request.body; // Fully typed!
// Process user creation...
reply.code(201);
return user;
}
)
});
// Register all routes with Fastify
router.initialize();
// Start the server
app.listen({ port: 3000 }, (err, address) => {
if (err) throw err;
console.log(`Server listening at ${address}`);
});
The main class for creating type-safe OpenAPI routes.
new OpenApiRouter<T>(app: FastifyInstance, document: T, options?: RouterOptions)
app: Fastify instancedocument: OpenAPI specification documentoptions: Optional configuration (see RouterOptions)route(path: string, methods: OperatorRecord)Registers a new route with the specified path and HTTP methods.
router.route("/users/:id", {
get: router.op(/* specification */, /* handler */),
put: router.op(/* specification */, /* handler */),
delete: router.op(/* specification */, /* handler */)
});
op<T>(specification: T, handler: FromSpec.Method<T>)Creates an operation handler with OpenAPI specification and type-safe handler function.
const operation = router.op(
{
summary: "Get user by ID",
parameters: [
{
name: "id",
in: "path",
required: true,
schema: { type: "integer" }
}
],
responses: {
200: {
description: "User object",
content: {
"application/json": {
schema: router.ref('#/components/schemas/User')
}
}
}
}
},
async (request, reply) => {
const { id } = request.params; // Fully typed!
// Implementation...
}
);
ref<S>(ref: S, options?: { useRef?: boolean })Creates a reference to a schema in the OpenAPI document.
// Get the actual schema object
const userSchema = router.ref('#/components/schemas/User');
// Get a $ref object
const userRef = router.ref('#/components/schemas/User', { useRef: true });
spec<T>(specification: T)Creates a new OpenAPI specification object.
const spec = router.spec({
summary: "Create a new user",
requestBody: {
required: true,
content: {
"application/json": {
schema: router.ref('#/components/schemas/User')
}
}
}
});
handler<T>(handler: FromSpec.Method<T>)Creates a handler function with the specified OpenAPI specification.
const handler = router.handler<typeof spec>(async (request) => {
return { id: 1, username: "alice" };
});
initialize()Initializes the router and registers all routes with Fastify.
router.initialize();
get specificationReturns the complete OpenAPI specification including all registered routes.
const spec = router.specification;
console.log(JSON.stringify(spec, null, 2));
interface RouterOptions {
specModifier?: (spec: OpenAPI.Operator) => OpenAPI.Operator;
autoValidate?: {
request?: {
validate?: boolean;
errorResponse?: {
status: number;
payload: Record<string, any> | ((errors: ErrorObject[]) => Record<string, any>);
};
};
response?: {
validate?: boolean;
errorResponse?: {
status: number;
payload: Record<string, any> | ((errors: ErrorObject[]) => Record<string, any>);
};
};
};
}
Enable automatic request and response validation:
const router = new OpenApiRouter(app, openApiDoc, {
autoValidate: {
request: {
validate: true,
errorResponse: {
status: 400,
payload: { error: "Invalid request body", errors: [] }
}
},
response: {
validate: true,
errorResponse: {
status: 500,
payload: { error: "Invalid response", errors: [] }
}
}
}
});
const router = new OpenApiRouter(app, openApiDoc, {
specModifier: (spec) => ({
...spec,
tags: ['api'],
security: [{ bearerAuth: [] }]
})
});
router.route("/users/:id/posts", {
get: router.op(
{
summary: "Get posts by user ID",
parameters: [
{
name: "id",
in: "path",
required: true,
schema: { type: "integer" }
},
{
name: "limit",
in: "query",
required: false,
schema: { type: "integer", minimum: 1, maximum: 100 }
}
],
responses: {
200: {
description: "List of posts",
content: {
"application/json": {
schema: {
type: "array",
items: {
type: "object",
properties: {
id: { type: "integer" },
title: { type: "string" },
content: { type: "string" }
}
}
}
}
}
}
}
},
async (request, reply) => {
const { id } = request.params;
const { limit } = request.query;
// Implementation...
}
)
});
The library provides full TypeScript support with compile-time type checking:
Built-in error handling for validation failures:
// Custom error responses
const router = new OpenApiRouter(app, openApiDoc, {
autoValidate: {
request: {
validate: true,
errorResponse: {
status: 422,
payload: (errors) => ({
error: "Validation failed",
details: errors
})
}
}
}
});
npm run build
npm test
Enable debug logging by setting the DEBUG environment variable:
DEBUG=true npm start
ISC
Contributions are welcome! Please feel free to submit a Pull Request.
For issues and questions, please visit the GitHub repository.