Contract Kit automatically generates OpenAPI 3.1 specifications from your contracts, providing comprehensive API documentation that works with tools like Swagger UI, Redoc, and Postman. This guide walks you through setting up OpenAPI generation, adding metadata, and serving interactive documentation.
Create an index file that exports all your contracts:
Copy
// contracts/index.tsexport * from "./todos";export * from "./users";import { todoContracts } from "./todos";import { userContracts } from "./users";// Export as a single array for OpenAPI generationexport const allContracts = [...todoContracts, ...userContracts];
Enhance your contracts with OpenAPI-specific metadata using builder methods:
Copy
// contracts/todos.tsimport { createContractGroup } from "contract-kit";import { z } from "zod";const todos = createContractGroup().namespace("todos");const TodoSchema = z.object({ id: z.string().uuid().describe("Unique identifier"), title: z.string().min(1).max(200).describe("Todo title"), completed: z.boolean().describe("Completion status"), createdAt: z.string().datetime().describe("Creation timestamp"),});export const getTodo = todos .get("/api/todos/:id") .path(z.object({ id: z.string().uuid().describe("Todo ID") })) .response(200, TodoSchema) .errors({ 404: z.object({ message: z.string() }).describe("Todo not found"), }) // OpenAPI metadata .summary("Get a todo by ID") .description("Retrieves a single todo item by its unique identifier") .tags("todos") .operationId("getTodo");export const listTodos = todos .get("/api/todos") .query(z.object({ completed: z.boolean().optional().describe("Filter by completion status"), limit: z.coerce.number().min(1).max(100).optional().describe("Maximum results per page"), offset: z.coerce.number().min(0).optional().describe("Pagination offset"), })) .response(200, z.object({ todos: z.array(TodoSchema), total: z.number(), })) .summary("List all todos") .description("Returns a paginated list of todos with optional filtering") .tags("todos") .operationId("listTodos");export const createTodo = todos .post("/api/todos") .body(z.object({ title: z.string().min(1).max(200).describe("Todo title"), completed: z.boolean().optional().describe("Initial completion status"), })) .response(201, TodoSchema) .summary("Create a new todo") .description("Creates a new todo item and returns it") .tags("todos") .operationId("createTodo");
Use .describe() on all schema fields and add .summary() and .description() to all operations. This creates comprehensive, self-documenting API specifications that are invaluable for API consumers.
Use meaningful operation IDs
Set custom operation IDs with .operationId() for better code generation in client SDKs. Use consistent naming conventions like listTodos, getTodo, createTodo.
Group operations with tags
Use tags to organize your API endpoints into logical groups. This makes navigation easier in documentation tools and helps consumers understand your API structure.
Document all error responses
Define comprehensive error schemas in .errors() to document all possible error responses. This helps API consumers handle errors properly.
Keep your spec up to date
Regenerate your OpenAPI spec whenever contracts change. Consider adding automated tests to ensure your spec stays in sync with your implementation.