Frameworks

Generate an SDK from tRPC with trpc-to-openapi

tRPC's whole design point is skipping OpenAPI: your frontend imports your backend's router type and gets end-to-end inference with no spec, no codegen, no REST layer. You only need an OpenAPI spec from a tRPC API if you have consumers who aren't TypeScript, or need documentation for procedures that a spec-based tool can read. For that case, use trpc-to-openapi — the original trpc-openapi package is archived on GitHub and hasn't been published since November 2024. From a generated spec, the rest is the same two-step problem as any other framework: get a clean document out, then run it through a generator or a hosted pipeline like Sourced.

trpc-openapi is archived — use trpc-to-openapi instead

trpc/trpc-openapi shows archived: true via the GitHub API, with its last push on 2024-11-19. trpc-to-openapi is a maintained fork; its own README says so directly: "This project is a fork of a fork, with full credit to the original authors. It appears that the original author has abandoned the project, so I plan to add new features in the near future." As of this writing the fork is at version 3.3.0 on npm, last published May 2026, with peer dependencies @trpc/server ^11.1.0 and zod ^3.25.0 || ^4.0.0. If a tutorial or Stack Overflow answer points you at trpc-openapi, swap the package name before you start — the API is close to identical.

Getting an OpenAPI spec out of tRPC with trpc-to-openapi

Step Code What it does
1. Install npm install trpc-to-openapi Adds the generator and its adapters
2. Add the meta type initTRPC.meta<OpenApiMeta>().create() Lets procedures declare openapi metadata
3. Tag procedures .meta({ openapi: { method: 'GET', path: '/say-hello' } }) Exposes one procedure as one REST endpoint
4. Generate the document generateOpenApiDocument(appRouter, { title, version, baseUrl }) Builds the OpenAPI object from tagged procedures
5. Mount a handler createOpenApiHttpHandler({ router: appRouter }) (also ships Express, Next.js, Fastify, and Nuxt adapters) Serves the REST endpoints tRPC itself doesn't provide
import { initTRPC } from '@trpc/server';
import { OpenApiMeta, generateOpenApiDocument } from 'trpc-to-openapi';
import { z } from 'zod';

const t = initTRPC.meta<OpenApiMeta>().create();

const appRouter = t.router({
  sayHello: t.procedure
    .meta({ openapi: { method: 'GET', path: '/say-hello' } })
    .input(z.object({ name: z.string() }))
    .output(z.object({ greeting: z.string() }))
    .query(({ input }) => ({ greeting: `Hello ${input.name}!` })),
});

export const openApiDocument = generateOpenApiDocument(appRouter, {
  title: 'tRPC OpenAPI',
  version: '1.0.0',
  baseUrl: 'http://localhost:3000',
});

A procedure only shows up in the generated document if it meets three requirements the README states directly: it must have an output parser that uses Zod validation, any input parser must also use Zod, and meta.openapi.method must be GET, POST, PATCH, PUT, or DELETE with a path starting with /. A procedure missing any of those is silently excluded rather than erroring.

Gotcha 1: no operationId field to set

OpenApiMeta's documented fields cover method, path, summary, description, tags, headers, and response codes — there's no operationId key. That means a tRPC OpenAPI document has no way to declare a stable method name at the source; whatever reads the document downstream (a generator, or Sourced) falls back to deriving one from the HTTP verb and path, the same fallback covered in fixing missing operationId errors. Set summary deliberately per procedure — it's the closest thing you control, and a good generator will prefer it over guessing from the path.

Gotcha 2: an endpoint silently disappears without a Zod output parser

Because generation is driven entirely by what each procedure declares, a procedure with no .output() parser — or one that skips Zod validation — never reaches the generated document, with no warning. This is the same trap as an endpoint missing from a hand-written OpenAPI file: nothing fails, the endpoint just isn't there. If a route you expect isn't showing up in generateOpenApiDocument's output, check for a missing or non-Zod output parser first.

From a tRPC spec to hosted docs and typed SDKs

Once generateOpenApiDocument produces a clean document — every public procedure tagged, Zod output parsers throughout — turning it into an installable SDK is a separate step. With Sourced:

  1. Point Sourced at your repo or the generated document. Connect from GitHub, or upload the JSON that generateOpenApiDocument returns directly.
  2. Preview the TypeScript and Python SDKs. Method names and typed models render before anything publishes, so a verb-and-path fallback name is visible in preview, not a customer's autocomplete.
  3. Get hosted docs and an llms.txt from the same pass. No separate docs deploy — the hosted docs preview and llms.txt come from the same spec.
  4. Publish on your schedule. Free unlimited previews and up to 2 hosted noindex docs review sites and one hosted MCP server, no credit card, cover steps 1-3.

Comparison: trpc-to-openapi vs. tRPC's native client vs. a full SDK pipeline

Need tRPC's native client trpc-to-openapi What neither gives you
Type-safe calls from a TypeScript frontend Yes, with zero spec Not its job
REST endpoints for non-TypeScript consumers No Yes, per tagged procedure
A generated OpenAPI spec No Yes Nothing — this part is real and free
A typed client SDK for another language No Nothing built in Generated Python (or other language) SDK
Agent-readable docs No Nothing built in llms.txt derived from the spec
A diff before a breaking release No Nothing built in A compatibility report

OSS generators for other languages

Sourced's generated SDKs focus on TypeScript and Python today — the two ecosystems where teams publish first, tRPC's own included. For every other target, a clean trpc-to-openapi document works with the standard per-language OpenAPI Generator ecosystem — Go, Java, Ruby, PHP, C#, Kotlin, Rust, and Swift all have their own walkthroughs. The five-minute generate-an-SDK guide covers running a local generator against any spec.

Other backend frameworks with their own spec-extraction paths: FastAPI, Rails, NestJS, Express, Django, Spring Boot, Laravel, and .NET.

Honest scope: when you don't need an OpenAPI spec from tRPC at all

If every consumer of your API is a TypeScript app that can import your AppRouter type, skip this entire post — that's what tRPC is for, and adding an OpenAPI layer on top is pure overhead. trpc-to-openapi earns its place only when you have a real non-TypeScript consumer (a mobile app, a partner integration, a public API) or need documentation that a spec-reading tool can consume. Even then, only tag the procedures you're actually exposing externally — meta.openapi is opt-in per procedure by design, so your internal-only procedures stay out of the generated document automatically.

FAQ

Is trpc-openapi still maintained?

No. trpc/trpc-openapi is archived on GitHub, with its last commit in November 2024. Use trpc-to-openapi, an actively maintained fork with the same core API, published as recently as May 2026 as of this writing.

Does tRPC generate OpenAPI automatically?

No, and it's not meant to. tRPC's model is type inference between a TypeScript backend and frontend with no spec involved. trpc-to-openapi adds an opt-in layer that exposes tagged procedures as REST endpoints with a generated OpenAPI document.

What does a procedure need to show up in the generated spec?

A meta.openapi block with a valid method and a path starting with /, an output parser using Zod validation, and — if present — an input parser that also uses Zod. Miss any of those and the procedure is silently excluded, not errored.

Why doesn't trpc-to-openapi let me set an operationId?

Its OpenApiMeta type doesn't expose one — only summary, description, tags, and similar fields. Set summary deliberately per procedure; it's the strongest signal you can give a downstream generator before it falls back to deriving a name from the verb and path.

Which frameworks can serve a trpc-to-openapi handler?

Express, Next.js, Fastify, and Nuxt have built-in adapters, plus generic Node http and Fetch-based handlers for other runtimes. AWS Lambda isn't supported as of this writing.

Do I need trpc-to-openapi if I only have TypeScript consumers?

No. tRPC's own client already gives you full type safety with no spec required. Reach for trpc-to-openapi only when a non-TypeScript consumer or a spec-based tool needs to read your API.

Ship it

Once generateOpenApiDocument produces a clean document from your tagged procedures, you have a real tRPC OpenAPI SDK source. Start free on Sourced to preview a TypeScript and Python SDK plus hosted docs from that spec in one pass, or run it through the OpenAPI validator first to catch gaps before you generate anything.