Authorization
The createContext function is called for each incoming request, so here you can add contextual information about the calling user from the request object.
Create context from request headers
server/context.tstsimport type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';import {decodeAndVerifyJwtToken } from './utils';export async functioncreateContext ({req ,res }:CreateHTTPContextOptions ) {// Create your context based on the request object// Will be available as `ctx` in all your resolvers// This is just an example of something you might want to do in your ctx fnasync functiongetUserFromHeader () {if (req .headers .authorization ) {constuser = awaitdecodeAndVerifyJwtToken (req .headers .authorization .split (' ')[1],);returnuser ;}return null;}constuser = awaitgetUserFromHeader ();return {user ,};}export typeContext =Awaited <ReturnType <typeofcreateContext >>;
server/context.tstsimport type {CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';import {decodeAndVerifyJwtToken } from './utils';export async functioncreateContext ({req ,res }:CreateHTTPContextOptions ) {// Create your context based on the request object// Will be available as `ctx` in all your resolvers// This is just an example of something you might want to do in your ctx fnasync functiongetUserFromHeader () {if (req .headers .authorization ) {constuser = awaitdecodeAndVerifyJwtToken (req .headers .authorization .split (' ')[1],);returnuser ;}return null;}constuser = awaitgetUserFromHeader ();return {user ,};}export typeContext =Awaited <ReturnType <typeofcreateContext >>;
Option 1: Authorize using resolver
server/routers/_app.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {z } from 'zod';typeContext = {user : {name : string } | null };export constt =initTRPC .context <Context >().create ();constappRouter =t .router ({// open for anyonehello :t .procedure .input (z .string ().nullish ()).query ((opts ) => `hello ${opts .input ??opts .ctx .user ?.name ?? 'world'}`),// checked in resolversecret :t .procedure .query ((opts ) => {if (!opts .ctx .user ) {throw newTRPCError ({code : 'UNAUTHORIZED' });}return {secret : 'sauce',};}),});
server/routers/_app.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {z } from 'zod';typeContext = {user : {name : string } | null };export constt =initTRPC .context <Context >().create ();constappRouter =t .router ({// open for anyonehello :t .procedure .input (z .string ().nullish ()).query ((opts ) => `hello ${opts .input ??opts .ctx .user ?.name ?? 'world'}`),// checked in resolversecret :t .procedure .query ((opts ) => {if (!opts .ctx .user ) {throw newTRPCError ({code : 'UNAUTHORIZED' });}return {secret : 'sauce',};}),});
Option 2: Authorize using middleware
server/routers/_app.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {z } from 'zod';typeContext = {user : {name : string } | null };export constt =initTRPC .context <Context >().create ();// you can reuse this for any procedureexport constprotectedProcedure =t .procedure .use (async functionisAuthed (opts ) {const {ctx } =opts ;// `ctx.user` is nullableif (!ctx .user ) {throw newTRPCError ({code : 'UNAUTHORIZED' });}returnopts .next ({ctx : {// ✅ user value is known to be non-null nowuser :ctx .user ,},});},);t .router ({// this is accessible for everyonehello :t .procedure .input (z .string ().nullish ()).query ((opts ) => `hello ${opts .input ??opts .ctx .user ?.name ?? 'world'}`),admin :t .router ({// this is accessible only to adminssecret :protectedProcedure .query ((opts ) => {return {secret : 'sauce',};}),}),});
server/routers/_app.tstsimport {initTRPC ,TRPCError } from '@trpc/server';import {z } from 'zod';typeContext = {user : {name : string } | null };export constt =initTRPC .context <Context >().create ();// you can reuse this for any procedureexport constprotectedProcedure =t .procedure .use (async functionisAuthed (opts ) {const {ctx } =opts ;// `ctx.user` is nullableif (!ctx .user ) {throw newTRPCError ({code : 'UNAUTHORIZED' });}returnopts .next ({ctx : {// ✅ user value is known to be non-null nowuser :ctx .user ,},});},);t .router ({// this is accessible for everyonehello :t .procedure .input (z .string ().nullish ()).query ((opts ) => `hello ${opts .input ??opts .ctx .user ?.name ?? 'world'}`),admin :t .router ({// this is accessible only to adminssecret :protectedProcedure .query ((opts ) => {return {secret : 'sauce',};}),}),});