---
title: OpenAPI
description: Generate OpenAPI document from the contract.
---

`createOpenApiDocument()` generates OpenAPI document object from HTTP
route declarations.

```ts
import { createOpenApiDocument } from "@rest-rpc/core";

const document = createOpenApiDocument(api, {
	info: {
		title: "Todo API",
		version: "1.0.0",
	},
	servers: [{ url: "https://api.example.com" }],
	schemaConverter,
});
```

## What Is Included

OpenAPI generation includes HTTP routes.

It maps:

- route methods and paths
- path params
- query params
- request headers
- request bodies
- status-keyed responses
- custom body content types
- route `openApi` metadata
- shared `commonOpenApi` metadata

WebSocket routes are skipped as there is no faithful mapping to OpenAPI.

## Schema Conversion

Standard Schema defines validation behavior, not JSON Schema conversion.

OpenAPI generation needs a project-provided `schemaConverter`.

```ts
import {
	createOpenApiDocument,
	isTypeOnlySchema,
	looseJsonSchema,
} from "@rest-rpc/core";
import { z } from "zod";

const document = createOpenApiDocument(api, {
	info: {
		title: "Todo API",
		version: "1.0.0",
	},
	schemaConverter: (schema, { io }) => {
		if (isTypeOnlySchema(schema)) {
			return looseJsonSchema(schema);
		}

		if (schema["~standard"].vendor === "zod") {
			return z.toJSONSchema(schema as z.ZodType, { io });
		}

		return looseJsonSchema(schema);
	},
});
```

Use a precise converter when your schema library supports JSON Schema output.
Use `looseJsonSchema()` when a loose OpenAPI shape is acceptable. If you have single validation library, you can just pass the library's JSON Schema converter directly.

## Route Metadata

Use `openApi` on a route for operation metadata.

```ts
create: {
	method: "POST",
	path: "/todos",
	body: {
		title: z.string().min(1),
	},
	response: todoSchema,
	openApi: {
		summary: "Create a todo",
		operationId: "createTodo",
		responses: {
			201: {
				description: "Todo created.",
			},
		},
	},
}
```

Status-keyed `responses` produce status-keyed OpenAPI responses.

```ts
create: {
	method: "POST",
	path: "/todos",
	body: {
		title: z.string().min(1),
	},
	responses: {
		201: todoSchema,
		409: z.object({
			code: z.literal("TODO_ALREADY_EXISTS"),
		}),
	},
	openApi: {
		summary: "Create a todo",
		operationId: "createTodo",
		responses: {
			201: {
				description: "Todo created.",
			},
			409: {
				description: "A todo with the same title already exists.",
			},
		},
	},
}
```

`openApi.responses` only affects generated OpenAPI output. It does not change
rest-rpc runtime behavior, handler return types, client response types,
validation, or server headers. Metadata for statuses that a route does not
declare is skipped.

```ts
openApi: {
	responses: {
		200: {
			headers: {
				"x-request-id": {
					description: "Request correlation id.",
					schema: z.string(),
				},
				"x-rate-limit": z.number(),
			},
		},
	},
}
```

Use route response `headers` instead when a handler-owned header is part of the
typed rest-rpc contract.

## Shared Metadata

Use `commonOpenApi` on `router()` options to apply metadata across a tree.

```ts
export const api = router(routes, {
	commonOpenApi: {
		tags: ["Todos"],
		security: [{ bearerAuth: [] }],
		responses: {
			200: {
				headers: {
					"x-request-id": z.string(),
				},
			},
			401: {
				description: "Authentication is required.",
			},
		},
	},
});
```

`commonOpenApi.responses` is merged into each route's `openApi.responses`.
Route-level OpenAPI metadata overrides common metadata. Typed headers declared
on a route response override OpenAPI-only headers with the same name in generated
OpenAPI.

## Transform Hooks

Use `transformOperation` and `transformDocument` when generated output needs
project-specific changes.

```ts
const document = createOpenApiDocument(api, {
	info: {
		title: "Todo API",
		version: "1.0.0",
	},
	schemaConverter,
	transformOperation: ({ route, operation }) => {
		if (route.metadata.auth === "required") {
			return {
				...operation,
				security: [{ bearerAuth: [] }],
			};
		}

		return operation;
	},
});
```
