Metadata
Procedure metadata allows you to add an optional procedure specific meta
property which will be available in all middleware function parameters.
tip
Use metadata together with trpc-openapi
if you want to expose REST-compatible endpoints for your application.
Create router with typed metadata
tsx
import { initTRPC } from '@trpc/server';// [...]interface Meta {authRequired: boolean;}export const t = initTRPC.context<Context>().meta<Meta>().create();export const appRouter = t.router({// [...]});
tsx
import { initTRPC } from '@trpc/server';// [...]interface Meta {authRequired: boolean;}export const t = initTRPC.context<Context>().meta<Meta>().create();export const appRouter = t.router({// [...]});
Example with per route authentication settings
server.tstsx
import { initTRPC } from '@trpc/server';// [...]interface Meta {authRequired: boolean;}export const t = initTRPC.context<Context>().meta<Meta>().create();export const authedProcedure = t.procedure.use(async (opts) => {const { meta, next, ctx } = opts;// only check authorization if enabledif (meta?.authRequired && !ctx.user) {throw new TRPCError({ code: 'UNAUTHORIZED' });}return next();});export const appRouter = t.router({hello: authedProcedure.meta({ authRequired: false }).query(() => {return {greeting: 'hello world',};}),protectedHello: authedProcedure.meta({ authRequired: true }).query(() => {return {greeting: 'hello-world',};}),});
server.tstsx
import { initTRPC } from '@trpc/server';// [...]interface Meta {authRequired: boolean;}export const t = initTRPC.context<Context>().meta<Meta>().create();export const authedProcedure = t.procedure.use(async (opts) => {const { meta, next, ctx } = opts;// only check authorization if enabledif (meta?.authRequired && !ctx.user) {throw new TRPCError({ code: 'UNAUTHORIZED' });}return next();});export const appRouter = t.router({hello: authedProcedure.meta({ authRequired: false }).query(() => {return {greeting: 'hello world',};}),protectedHello: authedProcedure.meta({ authRequired: true }).query(() => {return {greeting: 'hello-world',};}),});
Default meta, chaining, and shallow merging
You can set default values for your meta type, and if you chain meta on top of a base procedure it will be shallow merged.
tsx
import { initTRPC } from '@trpc/server';interface Meta {authRequired: boolean;role?: 'user' | 'admin'}export const t = initTRPC.context<Context>().meta<Meta>().create({// Set a default valuedefaultMeta: { authRequired: false }});const publicProcedure = t.procedure// ^ Default Meta: { authRequired: false }const authProcedure = publicProcedure.use(authMiddleware).meta({authRequired: true;role: 'user'});// ^ Meta: { authRequired: true, role: 'user' }const adminProcedure = authProcedure.meta({role: 'admin'});// ^ Meta: { authRequired: true, role: 'admin' }
tsx
import { initTRPC } from '@trpc/server';interface Meta {authRequired: boolean;role?: 'user' | 'admin'}export const t = initTRPC.context<Context>().meta<Meta>().create({// Set a default valuedefaultMeta: { authRequired: false }});const publicProcedure = t.procedure// ^ Default Meta: { authRequired: false }const authProcedure = publicProcedure.use(authMiddleware).meta({authRequired: true;role: 'user'});// ^ Meta: { authRequired: true, role: 'user' }const adminProcedure = authProcedure.meta({role: 'admin'});// ^ Meta: { authRequired: true, role: 'admin' }