OpenAPI
Expose tRPC procedures as REST endpoints
Overview
Expose tRPC procedures as REST endpoints using trpc-to-openapi. This allows external clients to consume your API without using tRPC.
Enabling OpenAPI
Add openapi metadata to a procedure to expose it as a REST endpoint:
import { authProcedure } from "@/trpc/procedures/trpc";
import { z } from "zod";
export const myRouter = createTRPCRouter({
list: authProcedure
.meta({
rateLimit: "QUERY",
openapi: {
enabled: true, // Required
method: "GET", // HTTP method
path: "/my-resources", // REST path
tags: ["MyResource"], // For grouping in docs
summary: "List resources",
description: "Returns all resources for the user",
protect: true, // Require authentication
},
})
.input(z.object({}))
.output(z.object({
success: z.boolean(),
payload: z.array(myResourceSchema),
}))
.query(async ({ ctx }) => {
return {
success: true,
payload: await ctx.db.myResource.list(),
};
}),
});OpenAPI procedures require explicit .input() and .output() schemas.
OpenAPI Metadata
Required Fields
| Field | Type | Description |
|---|---|---|
enabled | boolean | Enable OpenAPI exposure |
method | string | HTTP method (GET, POST) |
path | string | REST endpoint path |
tags | array | Tags for grouping |
summary | string | Short description |
protect | boolean | Require authentication |
Optional Fields
| Field | Type | Description |
|---|---|---|
description | string | Detailed description |
deprecated | boolean | Mark as deprecated |
OpenAPI Tags
Define tags in src/trpc/tags.ts:
export const OpenAPITags = {
MyResource: "My Resource",
AnotherResource: "Another Resource",
Admin: "Admin Operations",
} as const;Tags help organize endpoints in the OpenAPI documentation.
Examples
GET Endpoint
list: authProcedure
.meta({
rateLimit: "QUERY",
openapi: {
enabled: true,
method: "GET",
path: "/api-keys",
tags: ["API Keys"],
summary: "List API keys",
protect: true,
},
})
.input(z.object({}))
.output(z.object({
success: z.boolean(),
payload: z.array(apiKeySchema),
}))
.query(async ({ ctx }) => {
return {
success: true,
payload: await ctx.db.apiKeys.list(),
};
})POST Endpoint
create: authProcedure
.meta({
rateLimit: "MUTATION",
openapi: {
enabled: true,
method: "POST",
path: "/api-keys",
tags: ["API Keys"],
summary: "Create API key",
protect: true,
},
})
.input(z.object({
name: z.string().min(1),
}))
.output(z.object({
success: z.boolean(),
message: z.string().optional(),
payload: apiKeySchema,
}))
.mutation(async ({ ctx, input }) => {
const result = await ctx.db.apiKeys.create(input);
return {
success: true,
message: ctx.t("toasts.saved"),
payload: result,
};
})Public Endpoint
health: publicProcedure
.meta({
rateLimit: false,
openapi: {
enabled: true,
method: "GET",
path: "/health",
tags: ["System"],
summary: "Health check",
protect: false, // No authentication required
},
})
.input(z.object({}))
.output(z.object({
status: z.literal("ok"),
timestamp: z.string(),
}))
.query(() => {
return {
status: "ok",
timestamp: new Date().toISOString(),
};
})Accessing OpenAPI
OpenAPI JSON Document
The OpenAPI specification is available at:
GET /api/openapi.jsonREST Endpoints
All OpenAPI-enabled procedures are accessible at:
/api/<path>For example:
GET /api/api-keys- List API keysPOST /api/api-keys- Create API keyGET /api/health- Health check
Swagger UI
You can add Swagger UI using third-party packages to visualize and test the API.
Authentication
Protected endpoints (protect: true) require authentication via Bearer token:
curl -H "Authorization: Bearer YOUR_TOKEN" \
https://your-app.com/api/api-keysSee REST Endpoints for authentication details.
Disabling OpenAPI
To disable all OpenAPI endpoints:
// src/config/app.ts
export const AuthConfig = {
disableOpenAPI: true,
// ...
};