trpc-openapi
1.2.0 • Public • PublishedOpenAPI support fortRPC🧩
- Easy REST endpoints for your tRPC procedures.
- Perfect for incremental adoption.
- OpenAPI version 3.0.3.
Usage
1. Installtrpc-openapi
.
# npmnpm install trpc-openapi# yarnyarn add trpc-openapi
2. AddOpenApiMeta
to your tRPC instance.
import{initTRPC}from'@trpc/server';import{OpenApiMeta}from'trpc-openapi';constt=initTRPC.meta<OpenApiMeta>().create();/* 👈 */
3. Enableopenapi
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'trpc-openapi';import{appRouter}from'../appRouter';/* 👇 */exportconstopenApiDocument=generateOpenApiDocument(appRouter,{title:'tRPC OpenAPI',version:'1.0.0',baseUrl:'http://localhost:3000',});
5. Add antrpc-openapi
handler to your app.
We currently support adapters forExpress
,Next.js
,Serverless
,Fastify
,Nuxt
&Node:HTTP
.
Fetch
,Cloudflare Workers
& more soon™, PRs are welcomed
importhttpfrom'http';import{createOpenApiHttpHandler}from'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=James',{method:'GET'});constbody=awaitres.json();/* { greeting: 'Hello James!' } */
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 followingmust be true:
- Both
input
andoutput
parsers are present AND useZod
validation. - Query
input
parsers extendObject<{ [string]: String | Number | BigInt | Date }>
orVoid
. - Mutation
input
parsers extendObject<{ [string]: AnyType }>
orVoid
. meta.openapi.method
isGET
,POST
,PATCH
,PUT
orDELETE
.meta.openapi.path
is a string starting with/
.meta.openapi.path
parameters exist ininput
parser asString | Number | BigInt | Date
Please note:
- Data
transformers
(such assuperjson
) are ignored. - Trailing slashes are ignored.
- Routing is case-insensitive.
HTTP Requests
Procedures with aGET
/DELETE
method will accept inputs via URLquery parameters
. Procedures with aPOST
/PATCH
/PUT
method will accept inputs via therequest body
with aapplication/json
orapplication/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 themeta.openapi.path
field.
Query parameters
Query & path parameter inputs are always accepted as astring
. This library will attempt tocoerce your input values to the following primitive types out of the box:number
,boolean
,bigint
anddate
. If you wish to support others such asobject
,array
etc. please usez.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/James?greeting=Hello'/* 👈 */,{method:'GET',});constbody=awaitres.json();/* { greeting: 'Hello James!' } */
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/James'/* 👈 */,{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({greeting:'Hello'}),});constbody=awaitres.json();/* { greeting: 'Hello James!' } */
Custom headers
Any custom headers can be specified in themeta.openapi.headers
array, these headers will not be validated on request. Please consider usingAuthorization for first-class OpenAPI auth/security support.
HTTP Responses
Status codes will be200
by default for any successful requests. In the case of an error, the status code will be derived from the thrownTRPCError
or fallback to500
.
You can modify the status code or headers for any response using theresponseMeta
function.
Please seeerror status codes here.
Authorization
To create protected endpoints, addprotect: true
to themeta.openapi
object of each tRPC procedure. By default, you can then authenticate each request with thecreateContext
function using theAuthorization
header with theBearer
scheme. If you wish to authenticate requests using a different/additional methods (such as custom headers, or cookies) this can be overwritten by specifyingsecuritySchemes
object.
Explore acomplete example here.
Server
import{TRPCError,initTRPC}from'@trpc/server';import{OpenApiMeta}from'trpc-openapi';typeUser={id:string;name:string};constusers:User[]=[{id:'usr_123',name:'James',},];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 James!' } */
Examples
For advanced use-cases, please find examples in ourcomplete test suite.
With Express
Please seefull example here.
import{createExpressMiddleware}from'@trpc/server/adapters/express';importexpressfrom'express';import{createOpenApiExpressMiddleware}from'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 seefull example here.
// pages/api/[...trpc].tsimport{createOpenApiNextHandler}from'trpc-openapi';import{appRouter}from'../../server/appRouter';exportdefaultcreateOpenApiNextHandler({router:appRouter});
With AWS Lambda
Please seefull example here.
import{createOpenApiAwsLambdaHandler}from'trpc-openapi';import{appRouter}from'./appRouter';exportconstopenApi=createOpenApiAwsLambdaHandler({router:appRouter});
With Fastify
Please seefull example here.
import{fastifyTRPCPlugin}from'@trpc/server/adapters/fastify';importFastifyfrom'fastify';import{fastifyTRPCOpenApiPlugin}from'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 seefull typings here.
Property | Type | Description | Required |
---|---|---|---|
title | string | The title of the API. | true |
description | string | A short description of the API. | false |
version | string | The version of the OpenAPI document. | true |
baseUrl | string | The base URL of the target server. | true |
docsUrl | string | A URL to any external documentation. | false |
tags | string[] | A list for ordering endpoint groups. | false |
securitySchemes | Record<string, SecuritySchemeObject> | Defaults toAuthorization header withBearer scheme | false |
OpenApiMeta
Please seefull typings here.
Property | Type | Description | Required | Default |
---|---|---|---|---|
enabled | boolean | Exposes this procedure totrpc-openapi adapters and on the OpenAPI document. | false | true |
method | HttpMethod | HTTP method this endpoint is exposed on. Value can beGET ,POST ,PATCH ,PUT orDELETE . | true | undefined |
path | string | Pathname this endpoint is exposed on. Value must start with/ , specify path parameters using{} . | true | undefined |
protect | boolean | Requires this endpoint to use a security scheme. | false | false |
summary | string | A short summary of the endpoint included in the OpenAPI document. | false | undefined |
description | string | A verbose description of the endpoint included in the OpenAPI document. | false | undefined |
tags | string[] | A list of tags used for logical grouping of endpoints in the OpenAPI document. | false | undefined |
headers | ParameterObject[] | An array of custom headers to add for this endpoint in the OpenAPI document. | false | undefined |
contentTypes | ContentType[] | A set of content types specified as accepted in the OpenAPI document. | false | ['application/json'] |
deprecated | boolean | Whether or not to mark an endpoint as deprecated | false | false |
CreateOpenApiNodeHttpHandlerOptions
Please seefull typings here.
Property | Type | Description | Required |
---|---|---|---|
router | Router | Your application tRPC router. | true |
createContext | Function | Passes contextual (ctx ) data to procedure resolvers. | false |
responseMeta | Function | Returns any modifications to statusCode & headers. | false |
onError | Function | Called if error occurs inside handler. | false |
maxBodySize | number | Maximum 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.
Contact
James Berry - Follow me on Twitter@jlalmes
Package Sidebar
Install
npm i trpc-openapi
Repository
Homepage
Weekly Downloads
30,028
Version
1.2.0
License
MIT
Unpacked Size
123 kB
Total Files
83