Repository files navigation

@lilyrose2798/trpc-openapi

@lilyrose2798/trpc-openapi




OpenAPI support for tRPC 🧩

  • Easy REST endpoints for your tRPC procedures.
  • Perfect for incremental adoption.
  • Supports all OpenAPI versions.

Usage

1. Install @lilyrose2798/trpc-openapi.

# npm
npm install @lilyrose2798/trpc-openapi
# yarn
yarn add @lilyrose2798/trpc-openapi

2. Add OpenApiMeta to your tRPC instance.

import{initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';constt=initTRPC.meta<OpenApiMeta>().create();/* πŸ‘ˆ */

3. Enable openapi support for a procedure.

exportconstappRouter=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 })=>{return{greeting: `Hello ${input.name}!`};});});

4. Generate an OpenAPI document.

import{generateOpenApiDocument}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';/* πŸ‘‡ */exportconstopenApiDocument=generateOpenApiDocument(appRouter,{title: 'tRPC OpenAPI',version: '1.0.0',baseUrl: 'http://localhost:3000',});

5. Add an @lilyrose2798/trpc-openapi handler to your app.

We currently support adapters for Express, Next.js, Serverless, Fastify, Nuxt & Node:HTTP.

Fetch, Cloudflare Workers & more soonβ„’, PRs are welcomed πŸ™Œ.

importhttpfrom'http';import{createOpenApiHttpHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constserver=http.createServer(createOpenApiHttpHandler({router: appRouter}));/* πŸ‘ˆ */server.listen(3000);

6. Profit πŸ€‘

// client.tsconstres=awaitfetch('http://localhost:3000/say-hello?name=Lily',{method: 'GET'});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Requirements

Peer dependencies:

  • tRPC Server v10 (@trpc/server) must be installed.
  • Zod v3 (zod@^3.14.4) must be installed (recommended ^3.20.0).

For a procedure to support OpenAPI the following must be true:

  • Both input and output parsers are present AND use Zod validation.
  • Query input parsers extend Object<{ [string]: String | Number | BigInt | Date }> or Void.
  • Mutation input parsers extend Object<{ [string]: AnyType }> or Void.
  • meta.openapi.method is GET, POST, PATCH, PUT or DELETE.
  • meta.openapi.path is a string starting with /.
  • meta.openapi.path parameters exist in input parser as String | Number | BigInt | Date

Please note:

  • Data transformers (such as superjson) are ignored.
  • Trailing slashes are ignored.
  • Routing is case-insensitive.

HTTP Requests

Procedures with a GET/DELETE method will accept inputs via URL query parameters. Procedures with a POST/PATCH/PUT method will accept inputs via the request body with a application/json or application/x-www-form-urlencoded content type.

Path parameters

A procedure can accept a set of inputs via URL path parameters. You can add a path parameter to any OpenAPI procedure by using curly brackets around an input name as a path segment in the meta.openapi.path field.

Query parameters

Query & path parameter inputs are always accepted as a string. This library will attempt to coerce your input values to the following primitive types out of the box: number, boolean, bigint and date. If you wish to support others such as object, array etc. please use z.preprocess().

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).query(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily?greeting=Hello'/* πŸ‘ˆ */,{method: 'GET',});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Request body

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'POST',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).mutation(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily'/* πŸ‘ˆ */,{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({greeting: 'Hello'}),});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Custom headers

Any custom headers can be specified in the meta.openapi.requestHeaders and meta.openapi.responseHeaders zod object schema, these headers will not be validated. Please consider using Authorization for first-class OpenAPI auth/security support.

HTTP Responses

Status codes will be 200 by default for any successful requests. In the case of an error, the status code will be derived from the thrown TRPCError or fallback to 500.

You can modify the status code or headers for any response using the responseMeta function.

Please see error status codes here.

Authorization

To create protected endpoints, add protect: true to the meta.openapi object of each tRPC procedure. By default, you can then authenticate each request with the createContext function using the Authorization header with the Bearer scheme. If you wish to authenticate requests using a different/additional methods (such as custom headers, or cookies) this can be overwritten by specifying securitySchemes object.

Explore a complete example here.

Server

import{TRPCError,initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';typeUser={id: string;name: string};constusers: User[]=[{id: 'usr_123',name: 'Lily',},];exporttypeContext={user: User|null};exportconstcreateContext=async({ req, res }): Promise<Context>=>{letuser: User|null=null;if(req.headers.authorization){constuserId=req.headers.authorization.split(' ')[1];user=users.find((_user)=>_user.id===userId);}return{ user };};constt=initTRPC.context<Context>().meta<OpenApiMeta>().create();exportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello',protect: true/* πŸ‘ˆ */}}).input(z.void())// no input expected.output(z.object({greeting: z.string()})).query(({ input, ctx })=>{if(!ctx.user){thrownewTRPCError({message: 'User not found',code: 'UNAUTHORIZED'});}return{greeting: `Hello ${ctx.user.name}!`};}),});

Client

constres=awaitfetch('http://localhost:3000/say-hello',{method: 'GET',headers: {Authorization: 'Bearer usr_123'}/* πŸ‘ˆ */,});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Examples

For advanced use-cases, please find examples in our complete test suite.

With Express

Please see full example here.

import{createExpressMiddleware}from'@trpc/server/adapters/express';importexpressfrom'express';import{createOpenApiExpressMiddleware}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constapp=express();app.use('/api/trpc',createExpressMiddleware({router: appRouter}));app.use('/api',createOpenApiExpressMiddleware({router: appRouter}));/* πŸ‘ˆ */app.listen(3000);

With Next.js

Please see full example here.

// pages/api/[...trpc].tsimport{createOpenApiNextHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../../server/appRouter';exportdefaultcreateOpenApiNextHandler({router: appRouter});

With AWS Lambda

Please see full example here.

import{createOpenApiAwsLambdaHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./appRouter';exportconstopenApi=createOpenApiAwsLambdaHandler({router: appRouter});

With Fastify

Please see full example here.

import{fastifyTRPCPlugin}from'@trpc/server/adapters/fastify';importFastifyfrom'fastify';import{fastifyTRPCOpenApiPlugin}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./router';constfastify=Fastify();asyncfunctionmain(){awaitfastify.register(fastifyTRPCPlugin,{router: appRouter});awaitfastify.register(fastifyTRPCOpenApiPlugin,{router: appRouter});/* πŸ‘ˆ */awaitfastify.listen({port: 3000});}main();

Types

GenerateOpenApiDocumentOptions

Please see full typings here.

PropertyTypeDescriptionRequired
titlestringThe title of the API.true
descriptionstringA short description of the API.false
versionstringThe version of the OpenAPI document.true
baseUrlstringThe base URL of the target server.true
docsUrlstringA URL to any external documentation.false
tagsstring[]A list for ordering endpoint groups.false
securitySchemesRecord<string, SecuritySchemeObject>Defaults to Authorization header with Bearer schemefalse

OpenApiMeta

Please see full typings here.

PropertyTypeDescriptionRequiredDefault
enabledbooleanExposes this procedure to trpc-openapi adapters and on the OpenAPI document.falsetrue
methodHttpMethodHTTP method this endpoint is exposed on. Value can be GET, POST, PATCH, PUT or DELETE.trueundefined
pathstringPathname this endpoint is exposed on. Value must start with /, specify path parameters using {}.trueundefined
protectbooleanRequires this endpoint to use a security scheme.falsefalse
summarystringA short summary of the endpoint included in the OpenAPI document.falseundefined
descriptionstringA verbose description of the endpoint included in the OpenAPI document.falseundefined
tagsstring[]A list of tags used for logical grouping of endpoints in the OpenAPI document.falseundefined
requestHeadersAnyZodObjectA zod object schema describing any custom headers to add to the request for this endpoint in the OpenAPI document.falseundefined
responseHeadersAnyZodObjectA zod object schema describing any custom headers to add to the response for this endpoint in the OpenAPI document.falseundefined
successDescriptionstringA string to use as the description for a successful response.false'Successful response'
errorResponsesnumber[] | { [key: number]: string }A list of error response codes or an object of response codes and their description to add to the responses for this endpoint.falseundefined
contentTypesOpenApiContentType[]A set of content types specified as accepted in the OpenAPI document.false['application/json']
deprecatedbooleanWhether or not to mark an endpoint as deprecatedfalsefalse

CreateOpenApiNodeHttpHandlerOptions

Please see full typings here.

PropertyTypeDescriptionRequired
routerRouterYour application tRPC router.true
createContextFunctionPasses contextual (ctx) data to procedure resolvers.false
responseMetaFunctionReturns any modifications to statusCode & headers.false
onErrorFunctionCalled if error occurs inside handler.false
maxBodySizenumberMaximum request body size in bytes (default: 100kb).false

Still using tRPC v9? See our .interop() example.

License

Distributed under the MIT License. See LICENSE for more information.

About

OpenAPI support for tRPC 🧩

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

@lilyrose2798/trpc-openapi

@lilyrose2798/trpc-openapi




OpenAPI support for tRPC 🧩

  • Easy REST endpoints for your tRPC procedures.
  • Perfect for incremental adoption.
  • Supports all OpenAPI versions.

Usage

1. Install @lilyrose2798/trpc-openapi.

# npm
npm install @lilyrose2798/trpc-openapi
# yarn
yarn add @lilyrose2798/trpc-openapi

2. Add OpenApiMeta to your tRPC instance.

import{initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';constt=initTRPC.meta<OpenApiMeta>().create();/* πŸ‘ˆ */

3. Enable openapi support for a procedure.

exportconstappRouter=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 })=>{return{greeting: `Hello ${input.name}!`};});});

4. Generate an OpenAPI document.

import{generateOpenApiDocument}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';/* πŸ‘‡ */exportconstopenApiDocument=generateOpenApiDocument(appRouter,{title: 'tRPC OpenAPI',version: '1.0.0',baseUrl: 'http://localhost:3000',});

5. Add an @lilyrose2798/trpc-openapi handler to your app.

We currently support adapters for Express, Next.js, Serverless, Fastify, Nuxt & Node:HTTP.

Fetch, Cloudflare Workers & more soonβ„’, PRs are welcomed πŸ™Œ.

importhttpfrom'http';import{createOpenApiHttpHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constserver=http.createServer(createOpenApiHttpHandler({router: appRouter}));/* πŸ‘ˆ */server.listen(3000);

6. Profit πŸ€‘

// client.tsconstres=awaitfetch('http://localhost:3000/say-hello?name=Lily',{method: 'GET'});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Requirements

Peer dependencies:

  • tRPC Server v10 (@trpc/server) must be installed.
  • Zod v3 (zod@^3.14.4) must be installed (recommended ^3.20.0).

For a procedure to support OpenAPI the following must be true:

  • Both input and output parsers are present AND use Zod validation.
  • Query input parsers extend Object<{ [string]: String | Number | BigInt | Date }> or Void.
  • Mutation input parsers extend Object<{ [string]: AnyType }> or Void.
  • meta.openapi.method is GET, POST, PATCH, PUT or DELETE.
  • meta.openapi.path is a string starting with /.
  • meta.openapi.path parameters exist in input parser as String | Number | BigInt | Date

Please note:

  • Data transformers (such as superjson) are ignored.
  • Trailing slashes are ignored.
  • Routing is case-insensitive.

HTTP Requests

Procedures with a GET/DELETE method will accept inputs via URL query parameters. Procedures with a POST/PATCH/PUT method will accept inputs via the request body with a application/json or application/x-www-form-urlencoded content type.

Path parameters

A procedure can accept a set of inputs via URL path parameters. You can add a path parameter to any OpenAPI procedure by using curly brackets around an input name as a path segment in the meta.openapi.path field.

Query parameters

Query & path parameter inputs are always accepted as a string. This library will attempt to coerce your input values to the following primitive types out of the box: number, boolean, bigint and date. If you wish to support others such as object, array etc. please use z.preprocess().

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).query(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily?greeting=Hello'/* πŸ‘ˆ */,{method: 'GET',});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Request body

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'POST',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).mutation(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily'/* πŸ‘ˆ */,{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({greeting: 'Hello'}),});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Custom headers

Any custom headers can be specified in the meta.openapi.requestHeaders and meta.openapi.responseHeaders zod object schema, these headers will not be validated. Please consider using Authorization for first-class OpenAPI auth/security support.

HTTP Responses

Status codes will be 200 by default for any successful requests. In the case of an error, the status code will be derived from the thrown TRPCError or fallback to 500.

You can modify the status code or headers for any response using the responseMeta function.

Please see error status codes here.

Authorization

To create protected endpoints, add protect: true to the meta.openapi object of each tRPC procedure. By default, you can then authenticate each request with the createContext function using the Authorization header with the Bearer scheme. If you wish to authenticate requests using a different/additional methods (such as custom headers, or cookies) this can be overwritten by specifying securitySchemes object.

Explore a complete example here.

Server

import{TRPCError,initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';typeUser={id: string;name: string};constusers: User[]=[{id: 'usr_123',name: 'Lily',},];exporttypeContext={user: User|null};exportconstcreateContext=async({ req, res }): Promise<Context>=>{letuser: User|null=null;if(req.headers.authorization){constuserId=req.headers.authorization.split(' ')[1];user=users.find((_user)=>_user.id===userId);}return{ user };};constt=initTRPC.context<Context>().meta<OpenApiMeta>().create();exportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello',protect: true/* πŸ‘ˆ */}}).input(z.void())// no input expected.output(z.object({greeting: z.string()})).query(({ input, ctx })=>{if(!ctx.user){thrownewTRPCError({message: 'User not found',code: 'UNAUTHORIZED'});}return{greeting: `Hello ${ctx.user.name}!`};}),});

Client

constres=awaitfetch('http://localhost:3000/say-hello',{method: 'GET',headers: {Authorization: 'Bearer usr_123'}/* πŸ‘ˆ */,});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Examples

For advanced use-cases, please find examples in our complete test suite.

With Express

Please see full example here.

import{createExpressMiddleware}from'@trpc/server/adapters/express';importexpressfrom'express';import{createOpenApiExpressMiddleware}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constapp=express();app.use('/api/trpc',createExpressMiddleware({router: appRouter}));app.use('/api',createOpenApiExpressMiddleware({router: appRouter}));/* πŸ‘ˆ */app.listen(3000);

With Next.js

Please see full example here.

// pages/api/[...trpc].tsimport{createOpenApiNextHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../../server/appRouter';exportdefaultcreateOpenApiNextHandler({router: appRouter});

With AWS Lambda

Please see full example here.

import{createOpenApiAwsLambdaHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./appRouter';exportconstopenApi=createOpenApiAwsLambdaHandler({router: appRouter});

With Fastify

Please see full example here.

import{fastifyTRPCPlugin}from'@trpc/server/adapters/fastify';importFastifyfrom'fastify';import{fastifyTRPCOpenApiPlugin}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./router';constfastify=Fastify();asyncfunctionmain(){awaitfastify.register(fastifyTRPCPlugin,{router: appRouter});awaitfastify.register(fastifyTRPCOpenApiPlugin,{router: appRouter});/* πŸ‘ˆ */awaitfastify.listen({port: 3000});}main();

Types

GenerateOpenApiDocumentOptions

Please see full typings here.

PropertyTypeDescriptionRequired
titlestringThe title of the API.true
descriptionstringA short description of the API.false
versionstringThe version of the OpenAPI document.true
baseUrlstringThe base URL of the target server.true
docsUrlstringA URL to any external documentation.false
tagsstring[]A list for ordering endpoint groups.false
securitySchemesRecord<string, SecuritySchemeObject>Defaults to Authorization header with Bearer schemefalse

OpenApiMeta

Please see full typings here.

PropertyTypeDescriptionRequiredDefault
enabledbooleanExposes this procedure to trpc-openapi adapters and on the OpenAPI document.falsetrue
methodHttpMethodHTTP method this endpoint is exposed on. Value can be GET, POST, PATCH, PUT or DELETE.trueundefined
pathstringPathname this endpoint is exposed on. Value must start with /, specify path parameters using {}.trueundefined
protectbooleanRequires this endpoint to use a security scheme.falsefalse
summarystringA short summary of the endpoint included in the OpenAPI document.falseundefined
descriptionstringA verbose description of the endpoint included in the OpenAPI document.falseundefined
tagsstring[]A list of tags used for logical grouping of endpoints in the OpenAPI document.falseundefined
requestHeadersAnyZodObjectA zod object schema describing any custom headers to add to the request for this endpoint in the OpenAPI document.falseundefined
responseHeadersAnyZodObjectA zod object schema describing any custom headers to add to the response for this endpoint in the OpenAPI document.falseundefined
successDescriptionstringA string to use as the description for a successful response.false'Successful response'
errorResponsesnumber[] | { [key: number]: string }A list of error response codes or an object of response codes and their description to add to the responses for this endpoint.falseundefined
contentTypesOpenApiContentType[]A set of content types specified as accepted in the OpenAPI document.false['application/json']
deprecatedbooleanWhether or not to mark an endpoint as deprecatedfalsefalse

CreateOpenApiNodeHttpHandlerOptions

Please see full typings here.

PropertyTypeDescriptionRequired
routerRouterYour application tRPC router.true
createContextFunctionPasses contextual (ctx) data to procedure resolvers.false
responseMetaFunctionReturns any modifications to statusCode & headers.false
onErrorFunctionCalled if error occurs inside handler.false
maxBodySizenumberMaximum request body size in bytes (default: 100kb).false

Still using tRPC v9? See our .interop() example.

License

Distributed under the MIT License. See LICENSE for more information.

About

OpenAPI support for tRPC 🧩

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

@lilyrose2798/trpc-openapi

@lilyrose2798/trpc-openapi




OpenAPI support for tRPC 🧩

  • Easy REST endpoints for your tRPC procedures.
  • Perfect for incremental adoption.
  • Supports all OpenAPI versions.

Usage

1. Install @lilyrose2798/trpc-openapi.

# npm
npm install @lilyrose2798/trpc-openapi
# yarn
yarn add @lilyrose2798/trpc-openapi

2. Add OpenApiMeta to your tRPC instance.

import{initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';constt=initTRPC.meta<OpenApiMeta>().create();/* πŸ‘ˆ */

3. Enable openapi support for a procedure.

exportconstappRouter=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 })=>{return{greeting: `Hello ${input.name}!`};});});

4. Generate an OpenAPI document.

import{generateOpenApiDocument}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';/* πŸ‘‡ */exportconstopenApiDocument=generateOpenApiDocument(appRouter,{title: 'tRPC OpenAPI',version: '1.0.0',baseUrl: 'http://localhost:3000',});

5. Add an @lilyrose2798/trpc-openapi handler to your app.

We currently support adapters for Express, Next.js, Serverless, Fastify, Nuxt & Node:HTTP.

Fetch, Cloudflare Workers & more soonβ„’, PRs are welcomed πŸ™Œ.

importhttpfrom'http';import{createOpenApiHttpHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constserver=http.createServer(createOpenApiHttpHandler({router: appRouter}));/* πŸ‘ˆ */server.listen(3000);

6. Profit πŸ€‘

// client.tsconstres=awaitfetch('http://localhost:3000/say-hello?name=Lily',{method: 'GET'});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Requirements

Peer dependencies:

  • tRPC Server v10 (@trpc/server) must be installed.
  • Zod v3 (zod@^3.14.4) must be installed (recommended ^3.20.0).

For a procedure to support OpenAPI the following must be true:

  • Both input and output parsers are present AND use Zod validation.
  • Query input parsers extend Object<{ [string]: String | Number | BigInt | Date }> or Void.
  • Mutation input parsers extend Object<{ [string]: AnyType }> or Void.
  • meta.openapi.method is GET, POST, PATCH, PUT or DELETE.
  • meta.openapi.path is a string starting with /.
  • meta.openapi.path parameters exist in input parser as String | Number | BigInt | Date

Please note:

  • Data transformers (such as superjson) are ignored.
  • Trailing slashes are ignored.
  • Routing is case-insensitive.

HTTP Requests

Procedures with a GET/DELETE method will accept inputs via URL query parameters. Procedures with a POST/PATCH/PUT method will accept inputs via the request body with a application/json or application/x-www-form-urlencoded content type.

Path parameters

A procedure can accept a set of inputs via URL path parameters. You can add a path parameter to any OpenAPI procedure by using curly brackets around an input name as a path segment in the meta.openapi.path field.

Query parameters

Query & path parameter inputs are always accepted as a string. This library will attempt to coerce your input values to the following primitive types out of the box: number, boolean, bigint and date. If you wish to support others such as object, array etc. please use z.preprocess().

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).query(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily?greeting=Hello'/* πŸ‘ˆ */,{method: 'GET',});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Request body

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'POST',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).mutation(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily'/* πŸ‘ˆ */,{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({greeting: 'Hello'}),});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Custom headers

Any custom headers can be specified in the meta.openapi.requestHeaders and meta.openapi.responseHeaders zod object schema, these headers will not be validated. Please consider using Authorization for first-class OpenAPI auth/security support.

HTTP Responses

Status codes will be 200 by default for any successful requests. In the case of an error, the status code will be derived from the thrown TRPCError or fallback to 500.

You can modify the status code or headers for any response using the responseMeta function.

Please see error status codes here.

Authorization

To create protected endpoints, add protect: true to the meta.openapi object of each tRPC procedure. By default, you can then authenticate each request with the createContext function using the Authorization header with the Bearer scheme. If you wish to authenticate requests using a different/additional methods (such as custom headers, or cookies) this can be overwritten by specifying securitySchemes object.

Explore a complete example here.

Server

import{TRPCError,initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';typeUser={id: string;name: string};constusers: User[]=[{id: 'usr_123',name: 'Lily',},];exporttypeContext={user: User|null};exportconstcreateContext=async({ req, res }): Promise<Context>=>{letuser: User|null=null;if(req.headers.authorization){constuserId=req.headers.authorization.split(' ')[1];user=users.find((_user)=>_user.id===userId);}return{ user };};constt=initTRPC.context<Context>().meta<OpenApiMeta>().create();exportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello',protect: true/* πŸ‘ˆ */}}).input(z.void())// no input expected.output(z.object({greeting: z.string()})).query(({ input, ctx })=>{if(!ctx.user){thrownewTRPCError({message: 'User not found',code: 'UNAUTHORIZED'});}return{greeting: `Hello ${ctx.user.name}!`};}),});

Client

constres=awaitfetch('http://localhost:3000/say-hello',{method: 'GET',headers: {Authorization: 'Bearer usr_123'}/* πŸ‘ˆ */,});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Examples

For advanced use-cases, please find examples in our complete test suite.

With Express

Please see full example here.

import{createExpressMiddleware}from'@trpc/server/adapters/express';importexpressfrom'express';import{createOpenApiExpressMiddleware}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constapp=express();app.use('/api/trpc',createExpressMiddleware({router: appRouter}));app.use('/api',createOpenApiExpressMiddleware({router: appRouter}));/* πŸ‘ˆ */app.listen(3000);

With Next.js

Please see full example here.

// pages/api/[...trpc].tsimport{createOpenApiNextHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../../server/appRouter';exportdefaultcreateOpenApiNextHandler({router: appRouter});

With AWS Lambda

Please see full example here.

import{createOpenApiAwsLambdaHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./appRouter';exportconstopenApi=createOpenApiAwsLambdaHandler({router: appRouter});

With Fastify

Please see full example here.

import{fastifyTRPCPlugin}from'@trpc/server/adapters/fastify';importFastifyfrom'fastify';import{fastifyTRPCOpenApiPlugin}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./router';constfastify=Fastify();asyncfunctionmain(){awaitfastify.register(fastifyTRPCPlugin,{router: appRouter});awaitfastify.register(fastifyTRPCOpenApiPlugin,{router: appRouter});/* πŸ‘ˆ */awaitfastify.listen({port: 3000});}main();

Types

GenerateOpenApiDocumentOptions

Please see full typings here.

PropertyTypeDescriptionRequired
titlestringThe title of the API.true
descriptionstringA short description of the API.false
versionstringThe version of the OpenAPI document.true
baseUrlstringThe base URL of the target server.true
docsUrlstringA URL to any external documentation.false
tagsstring[]A list for ordering endpoint groups.false
securitySchemesRecord<string, SecuritySchemeObject>Defaults to Authorization header with Bearer schemefalse

OpenApiMeta

Please see full typings here.

PropertyTypeDescriptionRequiredDefault
enabledbooleanExposes this procedure to trpc-openapi adapters and on the OpenAPI document.falsetrue
methodHttpMethodHTTP method this endpoint is exposed on. Value can be GET, POST, PATCH, PUT or DELETE.trueundefined
pathstringPathname this endpoint is exposed on. Value must start with /, specify path parameters using {}.trueundefined
protectbooleanRequires this endpoint to use a security scheme.falsefalse
summarystringA short summary of the endpoint included in the OpenAPI document.falseundefined
descriptionstringA verbose description of the endpoint included in the OpenAPI document.falseundefined
tagsstring[]A list of tags used for logical grouping of endpoints in the OpenAPI document.falseundefined
requestHeadersAnyZodObjectA zod object schema describing any custom headers to add to the request for this endpoint in the OpenAPI document.falseundefined
responseHeadersAnyZodObjectA zod object schema describing any custom headers to add to the response for this endpoint in the OpenAPI document.falseundefined
successDescriptionstringA string to use as the description for a successful response.false'Successful response'
errorResponsesnumber[] | { [key: number]: string }A list of error response codes or an object of response codes and their description to add to the responses for this endpoint.falseundefined
contentTypesOpenApiContentType[]A set of content types specified as accepted in the OpenAPI document.false['application/json']
deprecatedbooleanWhether or not to mark an endpoint as deprecatedfalsefalse

CreateOpenApiNodeHttpHandlerOptions

Please see full typings here.

PropertyTypeDescriptionRequired
routerRouterYour application tRPC router.true
createContextFunctionPasses contextual (ctx) data to procedure resolvers.false
responseMetaFunctionReturns any modifications to statusCode & headers.false
onErrorFunctionCalled if error occurs inside handler.false
maxBodySizenumberMaximum request body size in bytes (default: 100kb).false

Still using tRPC v9? See our .interop() example.

License

Distributed under the MIT License. See LICENSE for more information.

About

OpenAPI support for tRPC 🧩

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

@lilyrose2798/trpc-openapi

@lilyrose2798/trpc-openapi




OpenAPI support for tRPC 🧩

  • Easy REST endpoints for your tRPC procedures.
  • Perfect for incremental adoption.
  • Supports all OpenAPI versions.

Usage

1. Install @lilyrose2798/trpc-openapi.

# npm
npm install @lilyrose2798/trpc-openapi
# yarn
yarn add @lilyrose2798/trpc-openapi

2. Add OpenApiMeta to your tRPC instance.

import{initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';constt=initTRPC.meta<OpenApiMeta>().create();/* πŸ‘ˆ */

3. Enable openapi support for a procedure.

exportconstappRouter=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 })=>{return{greeting: `Hello ${input.name}!`};});});

4. Generate an OpenAPI document.

import{generateOpenApiDocument}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';/* πŸ‘‡ */exportconstopenApiDocument=generateOpenApiDocument(appRouter,{title: 'tRPC OpenAPI',version: '1.0.0',baseUrl: 'http://localhost:3000',});

5. Add an @lilyrose2798/trpc-openapi handler to your app.

We currently support adapters for Express, Next.js, Serverless, Fastify, Nuxt & Node:HTTP.

Fetch, Cloudflare Workers & more soonβ„’, PRs are welcomed πŸ™Œ.

importhttpfrom'http';import{createOpenApiHttpHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constserver=http.createServer(createOpenApiHttpHandler({router: appRouter}));/* πŸ‘ˆ */server.listen(3000);

6. Profit πŸ€‘

// client.tsconstres=awaitfetch('http://localhost:3000/say-hello?name=Lily',{method: 'GET'});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Requirements

Peer dependencies:

  • tRPC Server v10 (@trpc/server) must be installed.
  • Zod v3 (zod@^3.14.4) must be installed (recommended ^3.20.0).

For a procedure to support OpenAPI the following must be true:

  • Both input and output parsers are present AND use Zod validation.
  • Query input parsers extend Object<{ [string]: String | Number | BigInt | Date }> or Void.
  • Mutation input parsers extend Object<{ [string]: AnyType }> or Void.
  • meta.openapi.method is GET, POST, PATCH, PUT or DELETE.
  • meta.openapi.path is a string starting with /.
  • meta.openapi.path parameters exist in input parser as String | Number | BigInt | Date

Please note:

  • Data transformers (such as superjson) are ignored.
  • Trailing slashes are ignored.
  • Routing is case-insensitive.

HTTP Requests

Procedures with a GET/DELETE method will accept inputs via URL query parameters. Procedures with a POST/PATCH/PUT method will accept inputs via the request body with a application/json or application/x-www-form-urlencoded content type.

Path parameters

A procedure can accept a set of inputs via URL path parameters. You can add a path parameter to any OpenAPI procedure by using curly brackets around an input name as a path segment in the meta.openapi.path field.

Query parameters

Query & path parameter inputs are always accepted as a string. This library will attempt to coerce your input values to the following primitive types out of the box: number, boolean, bigint and date. If you wish to support others such as object, array etc. please use z.preprocess().

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).query(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily?greeting=Hello'/* πŸ‘ˆ */,{method: 'GET',});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Request body

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'POST',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).mutation(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily'/* πŸ‘ˆ */,{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({greeting: 'Hello'}),});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Custom headers

Any custom headers can be specified in the meta.openapi.requestHeaders and meta.openapi.responseHeaders zod object schema, these headers will not be validated. Please consider using Authorization for first-class OpenAPI auth/security support.

HTTP Responses

Status codes will be 200 by default for any successful requests. In the case of an error, the status code will be derived from the thrown TRPCError or fallback to 500.

You can modify the status code or headers for any response using the responseMeta function.

Please see error status codes here.

Authorization

To create protected endpoints, add protect: true to the meta.openapi object of each tRPC procedure. By default, you can then authenticate each request with the createContext function using the Authorization header with the Bearer scheme. If you wish to authenticate requests using a different/additional methods (such as custom headers, or cookies) this can be overwritten by specifying securitySchemes object.

Explore a complete example here.

Server

import{TRPCError,initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';typeUser={id: string;name: string};constusers: User[]=[{id: 'usr_123',name: 'Lily',},];exporttypeContext={user: User|null};exportconstcreateContext=async({ req, res }): Promise<Context>=>{letuser: User|null=null;if(req.headers.authorization){constuserId=req.headers.authorization.split(' ')[1];user=users.find((_user)=>_user.id===userId);}return{ user };};constt=initTRPC.context<Context>().meta<OpenApiMeta>().create();exportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello',protect: true/* πŸ‘ˆ */}}).input(z.void())// no input expected.output(z.object({greeting: z.string()})).query(({ input, ctx })=>{if(!ctx.user){thrownewTRPCError({message: 'User not found',code: 'UNAUTHORIZED'});}return{greeting: `Hello ${ctx.user.name}!`};}),});

Client

constres=awaitfetch('http://localhost:3000/say-hello',{method: 'GET',headers: {Authorization: 'Bearer usr_123'}/* πŸ‘ˆ */,});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Examples

For advanced use-cases, please find examples in our complete test suite.

With Express

Please see full example here.

import{createExpressMiddleware}from'@trpc/server/adapters/express';importexpressfrom'express';import{createOpenApiExpressMiddleware}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constapp=express();app.use('/api/trpc',createExpressMiddleware({router: appRouter}));app.use('/api',createOpenApiExpressMiddleware({router: appRouter}));/* πŸ‘ˆ */app.listen(3000);

With Next.js

Please see full example here.

// pages/api/[...trpc].tsimport{createOpenApiNextHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../../server/appRouter';exportdefaultcreateOpenApiNextHandler({router: appRouter});

With AWS Lambda

Please see full example here.

import{createOpenApiAwsLambdaHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./appRouter';exportconstopenApi=createOpenApiAwsLambdaHandler({router: appRouter});

With Fastify

Please see full example here.

import{fastifyTRPCPlugin}from'@trpc/server/adapters/fastify';importFastifyfrom'fastify';import{fastifyTRPCOpenApiPlugin}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./router';constfastify=Fastify();asyncfunctionmain(){awaitfastify.register(fastifyTRPCPlugin,{router: appRouter});awaitfastify.register(fastifyTRPCOpenApiPlugin,{router: appRouter});/* πŸ‘ˆ */awaitfastify.listen({port: 3000});}main();

Types

GenerateOpenApiDocumentOptions

Please see full typings here.

PropertyTypeDescriptionRequired
titlestringThe title of the API.true
descriptionstringA short description of the API.false
versionstringThe version of the OpenAPI document.true
baseUrlstringThe base URL of the target server.true
docsUrlstringA URL to any external documentation.false
tagsstring[]A list for ordering endpoint groups.false
securitySchemesRecord<string, SecuritySchemeObject>Defaults to Authorization header with Bearer schemefalse

OpenApiMeta

Please see full typings here.

PropertyTypeDescriptionRequiredDefault
enabledbooleanExposes this procedure to trpc-openapi adapters and on the OpenAPI document.falsetrue
methodHttpMethodHTTP method this endpoint is exposed on. Value can be GET, POST, PATCH, PUT or DELETE.trueundefined
pathstringPathname this endpoint is exposed on. Value must start with /, specify path parameters using {}.trueundefined
protectbooleanRequires this endpoint to use a security scheme.falsefalse
summarystringA short summary of the endpoint included in the OpenAPI document.falseundefined
descriptionstringA verbose description of the endpoint included in the OpenAPI document.falseundefined
tagsstring[]A list of tags used for logical grouping of endpoints in the OpenAPI document.falseundefined
requestHeadersAnyZodObjectA zod object schema describing any custom headers to add to the request for this endpoint in the OpenAPI document.falseundefined
responseHeadersAnyZodObjectA zod object schema describing any custom headers to add to the response for this endpoint in the OpenAPI document.falseundefined
successDescriptionstringA string to use as the description for a successful response.false'Successful response'
errorResponsesnumber[] | { [key: number]: string }A list of error response codes or an object of response codes and their description to add to the responses for this endpoint.falseundefined
contentTypesOpenApiContentType[]A set of content types specified as accepted in the OpenAPI document.false['application/json']
deprecatedbooleanWhether or not to mark an endpoint as deprecatedfalsefalse

CreateOpenApiNodeHttpHandlerOptions

Please see full typings here.

PropertyTypeDescriptionRequired
routerRouterYour application tRPC router.true
createContextFunctionPasses contextual (ctx) data to procedure resolvers.false
responseMetaFunctionReturns any modifications to statusCode & headers.false
onErrorFunctionCalled if error occurs inside handler.false
maxBodySizenumberMaximum request body size in bytes (default: 100kb).false

Still using tRPC v9? See our .interop() example.

License

Distributed under the MIT License. See LICENSE for more information.

About

OpenAPI support for tRPC 🧩

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

@lilyrose2798/trpc-openapi

@lilyrose2798/trpc-openapi




OpenAPI support for tRPC 🧩

  • Easy REST endpoints for your tRPC procedures.
  • Perfect for incremental adoption.
  • Supports all OpenAPI versions.

Usage

1. Install @lilyrose2798/trpc-openapi.

# npm
npm install @lilyrose2798/trpc-openapi
# yarn
yarn add @lilyrose2798/trpc-openapi

2. Add OpenApiMeta to your tRPC instance.

import{initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';constt=initTRPC.meta<OpenApiMeta>().create();/* πŸ‘ˆ */

3. Enable openapi support for a procedure.

exportconstappRouter=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 })=>{return{greeting: `Hello ${input.name}!`};});});

4. Generate an OpenAPI document.

import{generateOpenApiDocument}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';/* πŸ‘‡ */exportconstopenApiDocument=generateOpenApiDocument(appRouter,{title: 'tRPC OpenAPI',version: '1.0.0',baseUrl: 'http://localhost:3000',});

5. Add an @lilyrose2798/trpc-openapi handler to your app.

We currently support adapters for Express, Next.js, Serverless, Fastify, Nuxt & Node:HTTP.

Fetch, Cloudflare Workers & more soonβ„’, PRs are welcomed πŸ™Œ.

importhttpfrom'http';import{createOpenApiHttpHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constserver=http.createServer(createOpenApiHttpHandler({router: appRouter}));/* πŸ‘ˆ */server.listen(3000);

6. Profit πŸ€‘

// client.tsconstres=awaitfetch('http://localhost:3000/say-hello?name=Lily',{method: 'GET'});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Requirements

Peer dependencies:

  • tRPC Server v10 (@trpc/server) must be installed.
  • Zod v3 (zod@^3.14.4) must be installed (recommended ^3.20.0).

For a procedure to support OpenAPI the following must be true:

  • Both input and output parsers are present AND use Zod validation.
  • Query input parsers extend Object<{ [string]: String | Number | BigInt | Date }> or Void.
  • Mutation input parsers extend Object<{ [string]: AnyType }> or Void.
  • meta.openapi.method is GET, POST, PATCH, PUT or DELETE.
  • meta.openapi.path is a string starting with /.
  • meta.openapi.path parameters exist in input parser as String | Number | BigInt | Date

Please note:

  • Data transformers (such as superjson) are ignored.
  • Trailing slashes are ignored.
  • Routing is case-insensitive.

HTTP Requests

Procedures with a GET/DELETE method will accept inputs via URL query parameters. Procedures with a POST/PATCH/PUT method will accept inputs via the request body with a application/json or application/x-www-form-urlencoded content type.

Path parameters

A procedure can accept a set of inputs via URL path parameters. You can add a path parameter to any OpenAPI procedure by using curly brackets around an input name as a path segment in the meta.openapi.path field.

Query parameters

Query & path parameter inputs are always accepted as a string. This library will attempt to coerce your input values to the following primitive types out of the box: number, boolean, bigint and date. If you wish to support others such as object, array etc. please use z.preprocess().

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).query(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily?greeting=Hello'/* πŸ‘ˆ */,{method: 'GET',});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Request body

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'POST',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).mutation(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily'/* πŸ‘ˆ */,{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({greeting: 'Hello'}),});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Custom headers

Any custom headers can be specified in the meta.openapi.requestHeaders and meta.openapi.responseHeaders zod object schema, these headers will not be validated. Please consider using Authorization for first-class OpenAPI auth/security support.

HTTP Responses

Status codes will be 200 by default for any successful requests. In the case of an error, the status code will be derived from the thrown TRPCError or fallback to 500.

You can modify the status code or headers for any response using the responseMeta function.

Please see error status codes here.

Authorization

To create protected endpoints, add protect: true to the meta.openapi object of each tRPC procedure. By default, you can then authenticate each request with the createContext function using the Authorization header with the Bearer scheme. If you wish to authenticate requests using a different/additional methods (such as custom headers, or cookies) this can be overwritten by specifying securitySchemes object.

Explore a complete example here.

Server

import{TRPCError,initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';typeUser={id: string;name: string};constusers: User[]=[{id: 'usr_123',name: 'Lily',},];exporttypeContext={user: User|null};exportconstcreateContext=async({ req, res }): Promise<Context>=>{letuser: User|null=null;if(req.headers.authorization){constuserId=req.headers.authorization.split(' ')[1];user=users.find((_user)=>_user.id===userId);}return{ user };};constt=initTRPC.context<Context>().meta<OpenApiMeta>().create();exportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello',protect: true/* πŸ‘ˆ */}}).input(z.void())// no input expected.output(z.object({greeting: z.string()})).query(({ input, ctx })=>{if(!ctx.user){thrownewTRPCError({message: 'User not found',code: 'UNAUTHORIZED'});}return{greeting: `Hello ${ctx.user.name}!`};}),});

Client

constres=awaitfetch('http://localhost:3000/say-hello',{method: 'GET',headers: {Authorization: 'Bearer usr_123'}/* πŸ‘ˆ */,});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Examples

For advanced use-cases, please find examples in our complete test suite.

With Express

Please see full example here.

import{createExpressMiddleware}from'@trpc/server/adapters/express';importexpressfrom'express';import{createOpenApiExpressMiddleware}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constapp=express();app.use('/api/trpc',createExpressMiddleware({router: appRouter}));app.use('/api',createOpenApiExpressMiddleware({router: appRouter}));/* πŸ‘ˆ */app.listen(3000);

With Next.js

Please see full example here.

// pages/api/[...trpc].tsimport{createOpenApiNextHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../../server/appRouter';exportdefaultcreateOpenApiNextHandler({router: appRouter});

With AWS Lambda

Please see full example here.

import{createOpenApiAwsLambdaHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./appRouter';exportconstopenApi=createOpenApiAwsLambdaHandler({router: appRouter});

With Fastify

Please see full example here.

import{fastifyTRPCPlugin}from'@trpc/server/adapters/fastify';importFastifyfrom'fastify';import{fastifyTRPCOpenApiPlugin}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./router';constfastify=Fastify();asyncfunctionmain(){awaitfastify.register(fastifyTRPCPlugin,{router: appRouter});awaitfastify.register(fastifyTRPCOpenApiPlugin,{router: appRouter});/* πŸ‘ˆ */awaitfastify.listen({port: 3000});}main();

Types

GenerateOpenApiDocumentOptions

Please see full typings here.

PropertyTypeDescriptionRequired
titlestringThe title of the API.true
descriptionstringA short description of the API.false
versionstringThe version of the OpenAPI document.true
baseUrlstringThe base URL of the target server.true
docsUrlstringA URL to any external documentation.false
tagsstring[]A list for ordering endpoint groups.false
securitySchemesRecord<string, SecuritySchemeObject>Defaults to Authorization header with Bearer schemefalse

OpenApiMeta

Please see full typings here.

PropertyTypeDescriptionRequiredDefault
enabledbooleanExposes this procedure to trpc-openapi adapters and on the OpenAPI document.falsetrue
methodHttpMethodHTTP method this endpoint is exposed on. Value can be GET, POST, PATCH, PUT or DELETE.trueundefined
pathstringPathname this endpoint is exposed on. Value must start with /, specify path parameters using {}.trueundefined
protectbooleanRequires this endpoint to use a security scheme.falsefalse
summarystringA short summary of the endpoint included in the OpenAPI document.falseundefined
descriptionstringA verbose description of the endpoint included in the OpenAPI document.falseundefined
tagsstring[]A list of tags used for logical grouping of endpoints in the OpenAPI document.falseundefined
requestHeadersAnyZodObjectA zod object schema describing any custom headers to add to the request for this endpoint in the OpenAPI document.falseundefined
responseHeadersAnyZodObjectA zod object schema describing any custom headers to add to the response for this endpoint in the OpenAPI document.falseundefined
successDescriptionstringA string to use as the description for a successful response.false'Successful response'
errorResponsesnumber[] | { [key: number]: string }A list of error response codes or an object of response codes and their description to add to the responses for this endpoint.falseundefined
contentTypesOpenApiContentType[]A set of content types specified as accepted in the OpenAPI document.false['application/json']
deprecatedbooleanWhether or not to mark an endpoint as deprecatedfalsefalse

CreateOpenApiNodeHttpHandlerOptions

Please see full typings here.

PropertyTypeDescriptionRequired
routerRouterYour application tRPC router.true
createContextFunctionPasses contextual (ctx) data to procedure resolvers.false
responseMetaFunctionReturns any modifications to statusCode & headers.false
onErrorFunctionCalled if error occurs inside handler.false
maxBodySizenumberMaximum request body size in bytes (default: 100kb).false

Still using tRPC v9? See our .interop() example.

License

Distributed under the MIT License. See LICENSE for more information.

About

OpenAPI support for tRPC 🧩

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

@lilyrose2798/trpc-openapi

@lilyrose2798/trpc-openapi




OpenAPI support for tRPC 🧩

  • Easy REST endpoints for your tRPC procedures.
  • Perfect for incremental adoption.
  • Supports all OpenAPI versions.

Usage

1. Install @lilyrose2798/trpc-openapi.

# npm
npm install @lilyrose2798/trpc-openapi
# yarn
yarn add @lilyrose2798/trpc-openapi

2. Add OpenApiMeta to your tRPC instance.

import{initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';constt=initTRPC.meta<OpenApiMeta>().create();/* πŸ‘ˆ */

3. Enable openapi support for a procedure.

exportconstappRouter=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 })=>{return{greeting: `Hello ${input.name}!`};});});

4. Generate an OpenAPI document.

import{generateOpenApiDocument}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';/* πŸ‘‡ */exportconstopenApiDocument=generateOpenApiDocument(appRouter,{title: 'tRPC OpenAPI',version: '1.0.0',baseUrl: 'http://localhost:3000',});

5. Add an @lilyrose2798/trpc-openapi handler to your app.

We currently support adapters for Express, Next.js, Serverless, Fastify, Nuxt & Node:HTTP.

Fetch, Cloudflare Workers & more soonβ„’, PRs are welcomed πŸ™Œ.

importhttpfrom'http';import{createOpenApiHttpHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constserver=http.createServer(createOpenApiHttpHandler({router: appRouter}));/* πŸ‘ˆ */server.listen(3000);

6. Profit πŸ€‘

// client.tsconstres=awaitfetch('http://localhost:3000/say-hello?name=Lily',{method: 'GET'});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Requirements

Peer dependencies:

  • tRPC Server v10 (@trpc/server) must be installed.
  • Zod v3 (zod@^3.14.4) must be installed (recommended ^3.20.0).

For a procedure to support OpenAPI the following must be true:

  • Both input and output parsers are present AND use Zod validation.
  • Query input parsers extend Object<{ [string]: String | Number | BigInt | Date }> or Void.
  • Mutation input parsers extend Object<{ [string]: AnyType }> or Void.
  • meta.openapi.method is GET, POST, PATCH, PUT or DELETE.
  • meta.openapi.path is a string starting with /.
  • meta.openapi.path parameters exist in input parser as String | Number | BigInt | Date

Please note:

  • Data transformers (such as superjson) are ignored.
  • Trailing slashes are ignored.
  • Routing is case-insensitive.

HTTP Requests

Procedures with a GET/DELETE method will accept inputs via URL query parameters. Procedures with a POST/PATCH/PUT method will accept inputs via the request body with a application/json or application/x-www-form-urlencoded content type.

Path parameters

A procedure can accept a set of inputs via URL path parameters. You can add a path parameter to any OpenAPI procedure by using curly brackets around an input name as a path segment in the meta.openapi.path field.

Query parameters

Query & path parameter inputs are always accepted as a string. This library will attempt to coerce your input values to the following primitive types out of the box: number, boolean, bigint and date. If you wish to support others such as object, array etc. please use z.preprocess().

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).query(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily?greeting=Hello'/* πŸ‘ˆ */,{method: 'GET',});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Request body

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'POST',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).mutation(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily'/* πŸ‘ˆ */,{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({greeting: 'Hello'}),});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Custom headers

Any custom headers can be specified in the meta.openapi.requestHeaders and meta.openapi.responseHeaders zod object schema, these headers will not be validated. Please consider using Authorization for first-class OpenAPI auth/security support.

HTTP Responses

Status codes will be 200 by default for any successful requests. In the case of an error, the status code will be derived from the thrown TRPCError or fallback to 500.

You can modify the status code or headers for any response using the responseMeta function.

Please see error status codes here.

Authorization

To create protected endpoints, add protect: true to the meta.openapi object of each tRPC procedure. By default, you can then authenticate each request with the createContext function using the Authorization header with the Bearer scheme. If you wish to authenticate requests using a different/additional methods (such as custom headers, or cookies) this can be overwritten by specifying securitySchemes object.

Explore a complete example here.

Server

import{TRPCError,initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';typeUser={id: string;name: string};constusers: User[]=[{id: 'usr_123',name: 'Lily',},];exporttypeContext={user: User|null};exportconstcreateContext=async({ req, res }): Promise<Context>=>{letuser: User|null=null;if(req.headers.authorization){constuserId=req.headers.authorization.split(' ')[1];user=users.find((_user)=>_user.id===userId);}return{ user };};constt=initTRPC.context<Context>().meta<OpenApiMeta>().create();exportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello',protect: true/* πŸ‘ˆ */}}).input(z.void())// no input expected.output(z.object({greeting: z.string()})).query(({ input, ctx })=>{if(!ctx.user){thrownewTRPCError({message: 'User not found',code: 'UNAUTHORIZED'});}return{greeting: `Hello ${ctx.user.name}!`};}),});

Client

constres=awaitfetch('http://localhost:3000/say-hello',{method: 'GET',headers: {Authorization: 'Bearer usr_123'}/* πŸ‘ˆ */,});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Examples

For advanced use-cases, please find examples in our complete test suite.

With Express

Please see full example here.

import{createExpressMiddleware}from'@trpc/server/adapters/express';importexpressfrom'express';import{createOpenApiExpressMiddleware}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constapp=express();app.use('/api/trpc',createExpressMiddleware({router: appRouter}));app.use('/api',createOpenApiExpressMiddleware({router: appRouter}));/* πŸ‘ˆ */app.listen(3000);

With Next.js

Please see full example here.

// pages/api/[...trpc].tsimport{createOpenApiNextHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../../server/appRouter';exportdefaultcreateOpenApiNextHandler({router: appRouter});

With AWS Lambda

Please see full example here.

import{createOpenApiAwsLambdaHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./appRouter';exportconstopenApi=createOpenApiAwsLambdaHandler({router: appRouter});

With Fastify

Please see full example here.

import{fastifyTRPCPlugin}from'@trpc/server/adapters/fastify';importFastifyfrom'fastify';import{fastifyTRPCOpenApiPlugin}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./router';constfastify=Fastify();asyncfunctionmain(){awaitfastify.register(fastifyTRPCPlugin,{router: appRouter});awaitfastify.register(fastifyTRPCOpenApiPlugin,{router: appRouter});/* πŸ‘ˆ */awaitfastify.listen({port: 3000});}main();

Types

GenerateOpenApiDocumentOptions

Please see full typings here.

PropertyTypeDescriptionRequired
titlestringThe title of the API.true
descriptionstringA short description of the API.false
versionstringThe version of the OpenAPI document.true
baseUrlstringThe base URL of the target server.true
docsUrlstringA URL to any external documentation.false
tagsstring[]A list for ordering endpoint groups.false
securitySchemesRecord<string, SecuritySchemeObject>Defaults to Authorization header with Bearer schemefalse

OpenApiMeta

Please see full typings here.

PropertyTypeDescriptionRequiredDefault
enabledbooleanExposes this procedure to trpc-openapi adapters and on the OpenAPI document.falsetrue
methodHttpMethodHTTP method this endpoint is exposed on. Value can be GET, POST, PATCH, PUT or DELETE.trueundefined
pathstringPathname this endpoint is exposed on. Value must start with /, specify path parameters using {}.trueundefined
protectbooleanRequires this endpoint to use a security scheme.falsefalse
summarystringA short summary of the endpoint included in the OpenAPI document.falseundefined
descriptionstringA verbose description of the endpoint included in the OpenAPI document.falseundefined
tagsstring[]A list of tags used for logical grouping of endpoints in the OpenAPI document.falseundefined
requestHeadersAnyZodObjectA zod object schema describing any custom headers to add to the request for this endpoint in the OpenAPI document.falseundefined
responseHeadersAnyZodObjectA zod object schema describing any custom headers to add to the response for this endpoint in the OpenAPI document.falseundefined
successDescriptionstringA string to use as the description for a successful response.false'Successful response'
errorResponsesnumber[] | { [key: number]: string }A list of error response codes or an object of response codes and their description to add to the responses for this endpoint.falseundefined
contentTypesOpenApiContentType[]A set of content types specified as accepted in the OpenAPI document.false['application/json']
deprecatedbooleanWhether or not to mark an endpoint as deprecatedfalsefalse

CreateOpenApiNodeHttpHandlerOptions

Please see full typings here.

PropertyTypeDescriptionRequired
routerRouterYour application tRPC router.true
createContextFunctionPasses contextual (ctx) data to procedure resolvers.false
responseMetaFunctionReturns any modifications to statusCode & headers.false
onErrorFunctionCalled if error occurs inside handler.false
maxBodySizenumberMaximum request body size in bytes (default: 100kb).false

Still using tRPC v9? See our .interop() example.

License

Distributed under the MIT License. See LICENSE for more information.

About

OpenAPI support for tRPC 🧩

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

@lilyrose2798/trpc-openapi

@lilyrose2798/trpc-openapi




OpenAPI support for tRPC 🧩

  • Easy REST endpoints for your tRPC procedures.
  • Perfect for incremental adoption.
  • Supports all OpenAPI versions.

Usage

1. Install @lilyrose2798/trpc-openapi.

# npm
npm install @lilyrose2798/trpc-openapi
# yarn
yarn add @lilyrose2798/trpc-openapi

2. Add OpenApiMeta to your tRPC instance.

import{initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';constt=initTRPC.meta<OpenApiMeta>().create();/* πŸ‘ˆ */

3. Enable openapi support for a procedure.

exportconstappRouter=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 })=>{return{greeting: `Hello ${input.name}!`};});});

4. Generate an OpenAPI document.

import{generateOpenApiDocument}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';/* πŸ‘‡ */exportconstopenApiDocument=generateOpenApiDocument(appRouter,{title: 'tRPC OpenAPI',version: '1.0.0',baseUrl: 'http://localhost:3000',});

5. Add an @lilyrose2798/trpc-openapi handler to your app.

We currently support adapters for Express, Next.js, Serverless, Fastify, Nuxt & Node:HTTP.

Fetch, Cloudflare Workers & more soonβ„’, PRs are welcomed πŸ™Œ.

importhttpfrom'http';import{createOpenApiHttpHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constserver=http.createServer(createOpenApiHttpHandler({router: appRouter}));/* πŸ‘ˆ */server.listen(3000);

6. Profit πŸ€‘

// client.tsconstres=awaitfetch('http://localhost:3000/say-hello?name=Lily',{method: 'GET'});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Requirements

Peer dependencies:

  • tRPC Server v10 (@trpc/server) must be installed.
  • Zod v3 (zod@^3.14.4) must be installed (recommended ^3.20.0).

For a procedure to support OpenAPI the following must be true:

  • Both input and output parsers are present AND use Zod validation.
  • Query input parsers extend Object<{ [string]: String | Number | BigInt | Date }> or Void.
  • Mutation input parsers extend Object<{ [string]: AnyType }> or Void.
  • meta.openapi.method is GET, POST, PATCH, PUT or DELETE.
  • meta.openapi.path is a string starting with /.
  • meta.openapi.path parameters exist in input parser as String | Number | BigInt | Date

Please note:

  • Data transformers (such as superjson) are ignored.
  • Trailing slashes are ignored.
  • Routing is case-insensitive.

HTTP Requests

Procedures with a GET/DELETE method will accept inputs via URL query parameters. Procedures with a POST/PATCH/PUT method will accept inputs via the request body with a application/json or application/x-www-form-urlencoded content type.

Path parameters

A procedure can accept a set of inputs via URL path parameters. You can add a path parameter to any OpenAPI procedure by using curly brackets around an input name as a path segment in the meta.openapi.path field.

Query parameters

Query & path parameter inputs are always accepted as a string. This library will attempt to coerce your input values to the following primitive types out of the box: number, boolean, bigint and date. If you wish to support others such as object, array etc. please use z.preprocess().

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).query(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily?greeting=Hello'/* πŸ‘ˆ */,{method: 'GET',});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Request body

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'POST',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).mutation(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily'/* πŸ‘ˆ */,{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({greeting: 'Hello'}),});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Custom headers

Any custom headers can be specified in the meta.openapi.requestHeaders and meta.openapi.responseHeaders zod object schema, these headers will not be validated. Please consider using Authorization for first-class OpenAPI auth/security support.

HTTP Responses

Status codes will be 200 by default for any successful requests. In the case of an error, the status code will be derived from the thrown TRPCError or fallback to 500.

You can modify the status code or headers for any response using the responseMeta function.

Please see error status codes here.

Authorization

To create protected endpoints, add protect: true to the meta.openapi object of each tRPC procedure. By default, you can then authenticate each request with the createContext function using the Authorization header with the Bearer scheme. If you wish to authenticate requests using a different/additional methods (such as custom headers, or cookies) this can be overwritten by specifying securitySchemes object.

Explore a complete example here.

Server

import{TRPCError,initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';typeUser={id: string;name: string};constusers: User[]=[{id: 'usr_123',name: 'Lily',},];exporttypeContext={user: User|null};exportconstcreateContext=async({ req, res }): Promise<Context>=>{letuser: User|null=null;if(req.headers.authorization){constuserId=req.headers.authorization.split(' ')[1];user=users.find((_user)=>_user.id===userId);}return{ user };};constt=initTRPC.context<Context>().meta<OpenApiMeta>().create();exportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello',protect: true/* πŸ‘ˆ */}}).input(z.void())// no input expected.output(z.object({greeting: z.string()})).query(({ input, ctx })=>{if(!ctx.user){thrownewTRPCError({message: 'User not found',code: 'UNAUTHORIZED'});}return{greeting: `Hello ${ctx.user.name}!`};}),});

Client

constres=awaitfetch('http://localhost:3000/say-hello',{method: 'GET',headers: {Authorization: 'Bearer usr_123'}/* πŸ‘ˆ */,});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Examples

For advanced use-cases, please find examples in our complete test suite.

With Express

Please see full example here.

import{createExpressMiddleware}from'@trpc/server/adapters/express';importexpressfrom'express';import{createOpenApiExpressMiddleware}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constapp=express();app.use('/api/trpc',createExpressMiddleware({router: appRouter}));app.use('/api',createOpenApiExpressMiddleware({router: appRouter}));/* πŸ‘ˆ */app.listen(3000);

With Next.js

Please see full example here.

// pages/api/[...trpc].tsimport{createOpenApiNextHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../../server/appRouter';exportdefaultcreateOpenApiNextHandler({router: appRouter});

With AWS Lambda

Please see full example here.

import{createOpenApiAwsLambdaHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./appRouter';exportconstopenApi=createOpenApiAwsLambdaHandler({router: appRouter});

With Fastify

Please see full example here.

import{fastifyTRPCPlugin}from'@trpc/server/adapters/fastify';importFastifyfrom'fastify';import{fastifyTRPCOpenApiPlugin}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./router';constfastify=Fastify();asyncfunctionmain(){awaitfastify.register(fastifyTRPCPlugin,{router: appRouter});awaitfastify.register(fastifyTRPCOpenApiPlugin,{router: appRouter});/* πŸ‘ˆ */awaitfastify.listen({port: 3000});}main();

Types

GenerateOpenApiDocumentOptions

Please see full typings here.

PropertyTypeDescriptionRequired
titlestringThe title of the API.true
descriptionstringA short description of the API.false
versionstringThe version of the OpenAPI document.true
baseUrlstringThe base URL of the target server.true
docsUrlstringA URL to any external documentation.false
tagsstring[]A list for ordering endpoint groups.false
securitySchemesRecord<string, SecuritySchemeObject>Defaults to Authorization header with Bearer schemefalse

OpenApiMeta

Please see full typings here.

PropertyTypeDescriptionRequiredDefault
enabledbooleanExposes this procedure to trpc-openapi adapters and on the OpenAPI document.falsetrue
methodHttpMethodHTTP method this endpoint is exposed on. Value can be GET, POST, PATCH, PUT or DELETE.trueundefined
pathstringPathname this endpoint is exposed on. Value must start with /, specify path parameters using {}.trueundefined
protectbooleanRequires this endpoint to use a security scheme.falsefalse
summarystringA short summary of the endpoint included in the OpenAPI document.falseundefined
descriptionstringA verbose description of the endpoint included in the OpenAPI document.falseundefined
tagsstring[]A list of tags used for logical grouping of endpoints in the OpenAPI document.falseundefined
requestHeadersAnyZodObjectA zod object schema describing any custom headers to add to the request for this endpoint in the OpenAPI document.falseundefined
responseHeadersAnyZodObjectA zod object schema describing any custom headers to add to the response for this endpoint in the OpenAPI document.falseundefined
successDescriptionstringA string to use as the description for a successful response.false'Successful response'
errorResponsesnumber[] | { [key: number]: string }A list of error response codes or an object of response codes and their description to add to the responses for this endpoint.falseundefined
contentTypesOpenApiContentType[]A set of content types specified as accepted in the OpenAPI document.false['application/json']
deprecatedbooleanWhether or not to mark an endpoint as deprecatedfalsefalse

CreateOpenApiNodeHttpHandlerOptions

Please see full typings here.

PropertyTypeDescriptionRequired
routerRouterYour application tRPC router.true
createContextFunctionPasses contextual (ctx) data to procedure resolvers.false
responseMetaFunctionReturns any modifications to statusCode & headers.false
onErrorFunctionCalled if error occurs inside handler.false
maxBodySizenumberMaximum request body size in bytes (default: 100kb).false

Still using tRPC v9? See our .interop() example.

License

Distributed under the MIT License. See LICENSE for more information.

About

OpenAPI support for tRPC 🧩

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

@lilyrose2798/trpc-openapi

@lilyrose2798/trpc-openapi




OpenAPI support for tRPC 🧩

  • Easy REST endpoints for your tRPC procedures.
  • Perfect for incremental adoption.
  • Supports all OpenAPI versions.

Usage

1. Install @lilyrose2798/trpc-openapi.

# npm
npm install @lilyrose2798/trpc-openapi
# yarn
yarn add @lilyrose2798/trpc-openapi

2. Add OpenApiMeta to your tRPC instance.

import{initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';constt=initTRPC.meta<OpenApiMeta>().create();/* πŸ‘ˆ */

3. Enable openapi support for a procedure.

exportconstappRouter=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 })=>{return{greeting: `Hello ${input.name}!`};});});

4. Generate an OpenAPI document.

import{generateOpenApiDocument}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';/* πŸ‘‡ */exportconstopenApiDocument=generateOpenApiDocument(appRouter,{title: 'tRPC OpenAPI',version: '1.0.0',baseUrl: 'http://localhost:3000',});

5. Add an @lilyrose2798/trpc-openapi handler to your app.

We currently support adapters for Express, Next.js, Serverless, Fastify, Nuxt & Node:HTTP.

Fetch, Cloudflare Workers & more soonβ„’, PRs are welcomed πŸ™Œ.

importhttpfrom'http';import{createOpenApiHttpHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constserver=http.createServer(createOpenApiHttpHandler({router: appRouter}));/* πŸ‘ˆ */server.listen(3000);

6. Profit πŸ€‘

// client.tsconstres=awaitfetch('http://localhost:3000/say-hello?name=Lily',{method: 'GET'});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Requirements

Peer dependencies:

  • tRPC Server v10 (@trpc/server) must be installed.
  • Zod v3 (zod@^3.14.4) must be installed (recommended ^3.20.0).

For a procedure to support OpenAPI the following must be true:

  • Both input and output parsers are present AND use Zod validation.
  • Query input parsers extend Object<{ [string]: String | Number | BigInt | Date }> or Void.
  • Mutation input parsers extend Object<{ [string]: AnyType }> or Void.
  • meta.openapi.method is GET, POST, PATCH, PUT or DELETE.
  • meta.openapi.path is a string starting with /.
  • meta.openapi.path parameters exist in input parser as String | Number | BigInt | Date

Please note:

  • Data transformers (such as superjson) are ignored.
  • Trailing slashes are ignored.
  • Routing is case-insensitive.

HTTP Requests

Procedures with a GET/DELETE method will accept inputs via URL query parameters. Procedures with a POST/PATCH/PUT method will accept inputs via the request body with a application/json or application/x-www-form-urlencoded content type.

Path parameters

A procedure can accept a set of inputs via URL path parameters. You can add a path parameter to any OpenAPI procedure by using curly brackets around an input name as a path segment in the meta.openapi.path field.

Query parameters

Query & path parameter inputs are always accepted as a string. This library will attempt to coerce your input values to the following primitive types out of the box: number, boolean, bigint and date. If you wish to support others such as object, array etc. please use z.preprocess().

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).query(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily?greeting=Hello'/* πŸ‘ˆ */,{method: 'GET',});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Request body

// RouterexportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'POST',path: '/say-hello/{name}'/* πŸ‘ˆ */}}).input(z.object({name: z.string()/* πŸ‘ˆ */,greeting: z.string()})).output(z.object({greeting: z.string()})).mutation(({ input })=>{return{greeting: `${input.greeting}${input.name}!`};});});// Clientconstres=awaitfetch('http://localhost:3000/say-hello/Lily'/* πŸ‘ˆ */,{method: 'POST',headers: {'Content-Type': 'application/json'},body: JSON.stringify({greeting: 'Hello'}),});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Custom headers

Any custom headers can be specified in the meta.openapi.requestHeaders and meta.openapi.responseHeaders zod object schema, these headers will not be validated. Please consider using Authorization for first-class OpenAPI auth/security support.

HTTP Responses

Status codes will be 200 by default for any successful requests. In the case of an error, the status code will be derived from the thrown TRPCError or fallback to 500.

You can modify the status code or headers for any response using the responseMeta function.

Please see error status codes here.

Authorization

To create protected endpoints, add protect: true to the meta.openapi object of each tRPC procedure. By default, you can then authenticate each request with the createContext function using the Authorization header with the Bearer scheme. If you wish to authenticate requests using a different/additional methods (such as custom headers, or cookies) this can be overwritten by specifying securitySchemes object.

Explore a complete example here.

Server

import{TRPCError,initTRPC}from'@trpc/server';import{OpenApiMeta}from'@lilyrose2798/trpc-openapi';typeUser={id: string;name: string};constusers: User[]=[{id: 'usr_123',name: 'Lily',},];exporttypeContext={user: User|null};exportconstcreateContext=async({ req, res }): Promise<Context>=>{letuser: User|null=null;if(req.headers.authorization){constuserId=req.headers.authorization.split(' ')[1];user=users.find((_user)=>_user.id===userId);}return{ user };};constt=initTRPC.context<Context>().meta<OpenApiMeta>().create();exportconstappRouter=t.router({sayHello: t.procedure.meta({openapi: {method: 'GET',path: '/say-hello',protect: true/* πŸ‘ˆ */}}).input(z.void())// no input expected.output(z.object({greeting: z.string()})).query(({ input, ctx })=>{if(!ctx.user){thrownewTRPCError({message: 'User not found',code: 'UNAUTHORIZED'});}return{greeting: `Hello ${ctx.user.name}!`};}),});

Client

constres=awaitfetch('http://localhost:3000/say-hello',{method: 'GET',headers: {Authorization: 'Bearer usr_123'}/* πŸ‘ˆ */,});constbody=awaitres.json();/* { greeting: 'Hello Lily!' } */

Examples

For advanced use-cases, please find examples in our complete test suite.

With Express

Please see full example here.

import{createExpressMiddleware}from'@trpc/server/adapters/express';importexpressfrom'express';import{createOpenApiExpressMiddleware}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../appRouter';constapp=express();app.use('/api/trpc',createExpressMiddleware({router: appRouter}));app.use('/api',createOpenApiExpressMiddleware({router: appRouter}));/* πŸ‘ˆ */app.listen(3000);

With Next.js

Please see full example here.

// pages/api/[...trpc].tsimport{createOpenApiNextHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'../../server/appRouter';exportdefaultcreateOpenApiNextHandler({router: appRouter});

With AWS Lambda

Please see full example here.

import{createOpenApiAwsLambdaHandler}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./appRouter';exportconstopenApi=createOpenApiAwsLambdaHandler({router: appRouter});

With Fastify

Please see full example here.

import{fastifyTRPCPlugin}from'@trpc/server/adapters/fastify';importFastifyfrom'fastify';import{fastifyTRPCOpenApiPlugin}from'@lilyrose2798/trpc-openapi';import{appRouter}from'./router';constfastify=Fastify();asyncfunctionmain(){awaitfastify.register(fastifyTRPCPlugin,{router: appRouter});awaitfastify.register(fastifyTRPCOpenApiPlugin,{router: appRouter});/* πŸ‘ˆ */awaitfastify.listen({port: 3000});}main();

Types

GenerateOpenApiDocumentOptions

Please see full typings here.

PropertyTypeDescriptionRequired
titlestringThe title of the API.true
descriptionstringA short description of the API.false
versionstringThe version of the OpenAPI document.true
baseUrlstringThe base URL of the target server.true
docsUrlstringA URL to any external documentation.false
tagsstring[]A list for ordering endpoint groups.false
securitySchemesRecord<string, SecuritySchemeObject>Defaults to Authorization header with Bearer schemefalse

OpenApiMeta

Please see full typings here.

PropertyTypeDescriptionRequiredDefault
enabledbooleanExposes this procedure to trpc-openapi adapters and on the OpenAPI document.falsetrue
methodHttpMethodHTTP method this endpoint is exposed on. Value can be GET, POST, PATCH, PUT or DELETE.trueundefined
pathstringPathname this endpoint is exposed on. Value must start with /, specify path parameters using {}.trueundefined
protectbooleanRequires this endpoint to use a security scheme.falsefalse
summarystringA short summary of the endpoint included in the OpenAPI document.falseundefined
descriptionstringA verbose description of the endpoint included in the OpenAPI document.falseundefined
tagsstring[]A list of tags used for logical grouping of endpoints in the OpenAPI document.falseundefined
requestHeadersAnyZodObjectA zod object schema describing any custom headers to add to the request for this endpoint in the OpenAPI document.falseundefined
responseHeadersAnyZodObjectA zod object schema describing any custom headers to add to the response for this endpoint in the OpenAPI document.falseundefined
successDescriptionstringA string to use as the description for a successful response.false'Successful response'
errorResponsesnumber[] | { [key: number]: string }A list of error response codes or an object of response codes and their description to add to the responses for this endpoint.falseundefined
contentTypesOpenApiContentType[]A set of content types specified as accepted in the OpenAPI document.false['application/json']
deprecatedbooleanWhether or not to mark an endpoint as deprecatedfalsefalse

CreateOpenApiNodeHttpHandlerOptions

Please see full typings here.

PropertyTypeDescriptionRequired
routerRouterYour application tRPC router.true
createContextFunctionPasses contextual (ctx) data to procedure resolvers.false
responseMetaFunctionReturns any modifications to statusCode & headers.false
onErrorFunctionCalled if error occurs inside handler.false
maxBodySizenumberMaximum request body size in bytes (default: 100kb).false

Still using tRPC v9? See our .interop() example.

License

Distributed under the MIT License. See LICENSE for more information.

About

OpenAPI support for tRPC 🧩

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages