Authorization
On this page
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.ts
import type { type CreateHTTPContextOptions = {
req: http.IncomingMessage;
res: http.ServerResponse;
info: TRPCRequestInfo;
}
CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
import { function decodeAndVerifyJwtToken(token: string): Promise<{
name: string;
}>
decodeAndVerifyJwtToken } from './utils';
export async function function createContext({ req, res }: CreateHTTPContextOptions): Promise<{
user: {
name: string;
} | null;
}>
createContext({ req: http.IncomingMessagereq, res: http.ServerResponseres }: type CreateHTTPContextOptions = {
req: http.IncomingMessage;
res: http.ServerResponse;
info: TRPCRequestInfo;
}
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 fn
async function function (local function) getUserFromHeader(): Promise<{
name: string;
} | null>
getUserFromHeader() {
if (req: http.IncomingMessagereq.headers.authorization) {
const const user: {
name: string;
}
user = await function decodeAndVerifyJwtToken(token: string): Promise<{
name: string;
}>
decodeAndVerifyJwtToken(
req: http.IncomingMessagereq.headers.authorization.split(' ')[1],
);
return const user: {
name: string;
}
user;
}
return null;
}
const const user: {
name: string;
} | null
user = await function (local function) getUserFromHeader(): Promise<{
name: string;
} | null>
getUserFromHeader();
return {
user: {
name: string;
} | null
user,
};
}
export type type Context = {
user: {
name: string;
} | null;
}
Context = type Awaited<T> = T extends null | undefined ? T : T extends object & {
then(onfulfilled: infer F, ...args: infer _): any;
} ? F extends (value: infer V, ...args: infer _) => any ? Awaited<V> : never : T
Awaited<type ReturnType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : anyReturnType<typeof function createContext({ req, res }: CreateHTTPContextOptions): Promise<{
user: {
name: string;
} | null;
}>
createContext>>;Option 1: Authorize using resolver
server/routers/_app.ts
import { const initTRPC: TRPCBuilder<object, object>initTRPC, class TRPCErrorTRPCError } from '@trpc/server';
import { import zz } from 'zod';
type type Context = {
user: {
name: string;
} | null;
}
Context = { user: {
name: string;
} | null
user: { name: stringname: string } | null };
export const const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC.TRPCBuilder<object, object>.context<Context>(): TRPCBuilder<Context, object>context<type Context = {
user: {
name: string;
} | null;
}
Context>().TRPCBuilder<Context, object>.create<RuntimeConfigOptions<Context, object>>(opts?: RuntimeConfigOptions<Context, object> | undefined): TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
create();
const const appRouter: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
hello: QueryProcedure<{
input: string | null | undefined;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
appRouter = const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder
<{
hello: QueryProcedure<{
input: string | null | undefined;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>(_: {
hello: QueryProcedure<{
input: string | null | undefined;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}) => BuiltRouter<...>
router({
// open for anyone
hello: QueryProcedure<{
input: string | null | undefined;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>
hello: const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>procedure
.ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.input<z.ZodOptional<z.ZodNullable<z.ZodString>>>(schema: z.ZodOptional<z.ZodNullable<z.ZodString>>): ProcedureBuilder<Context, object, object, string | null | undefined, string | null | undefined, UnsetMarker, UnsetMarker, false, DefaultErrorShape>input(import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string().ZodType<any, any, $ZodStringInternals<string>>.nullish(): z.ZodOptional<z.ZodNullable<z.ZodString>>nullish())
.ProcedureBuilder<Context, object, object, string | null | undefined, string | null | undefined, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<string>(resolver: ProcedureResolver<Context, object, object, string | null | undefined, UnsetMarker, string>): QueryProcedure<{
input: string | null | undefined;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>
query((opts: ProcedureResolverOptions<Context, object, object, string | null | undefined>opts) => `hello ${opts: ProcedureResolverOptions<Context, object, object, string | null | undefined>opts.ProcedureResolverOptions<Context, object, object, string | null | undefined>.input: string | null | undefinedinput ?? opts: ProcedureResolverOptions<Context, object, object, string | null | undefined>opts.ProcedureResolverOptions<Context, object, object, string | null | undefined>.ctx: {
user: {
name: string;
} | null;
}
ctx.user: {
name: string;
} | null
user?.name: string | undefinedname ?? 'world'}`),
// checked in resolver
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>
secret: const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>procedure.ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<{
secret: string;
}>(resolver: ProcedureResolver<Context, object, object, UnsetMarker, UnsetMarker, {
secret: string;
}>): QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>
query((opts: ProcedureResolverOptions<Context, object, object, UnsetMarker>opts) => {
if (!opts: ProcedureResolverOptions<Context, object, object, UnsetMarker>opts.ProcedureResolverOptions<Context, object, object, UnsetMarker>.ctx: {
user: {
name: string;
} | null;
}
ctx.user: {
name: string;
} | null
user) {
throw new new TRPCError(opts: {
message?: string;
code: TRPC_ERROR_CODE_KEY;
cause?: unknown;
}): TRPCError
TRPCError({ code: "UNAUTHORIZED" | "PARSE_ERROR" | "BAD_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "NOT_FOUND" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "PRECONDITION_REQUIRED" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST"code: 'UNAUTHORIZED' });
}
return {
secret: stringsecret: 'sauce',
};
}),
});Option 2: Authorize using middleware
server/routers/_app.ts
import { const initTRPC: TRPCBuilder<object, object>initTRPC, class TRPCErrorTRPCError } from '@trpc/server';
import { import zz } from 'zod';
type type Context = {
user: {
name: string;
} | null;
}
Context = { user: {
name: string;
} | null
user: { name: stringname: string } | null };
export const const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC.TRPCBuilder<object, object>.context<Context>(): TRPCBuilder<Context, object>context<type Context = {
user: {
name: string;
} | null;
}
Context>().TRPCBuilder<Context, object>.create<RuntimeConfigOptions<Context, object>>(opts?: RuntimeConfigOptions<Context, object> | undefined): TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
create();
// you can reuse this for any procedure
export const const protectedProcedure: ProcedureBuilder<Context, object, {
user: {
name: string;
};
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
protectedProcedure = const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>procedure.ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.use<{
user: {
name: string;
};
}>(fn: MiddlewareBuilder<{
user: {
name: string;
} | null;
}, object, {
user: {
name: string;
};
}, UnsetMarker> | MiddlewareFunction<Context, object, object, {
user: {
name: string;
};
}, UnsetMarker>): ProcedureBuilder<Context, object, {
user: {
name: string;
};
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
use(
async function function (local function) isAuthed(opts: {
ctx: {
user: {
name: string;
} | null;
};
type: ProcedureType;
path: string;
input: UnsetMarker;
getRawInput: GetRawInputFn;
meta: object | undefined;
signal: AbortSignal | undefined;
batchIndex: number;
next: {
(): Promise<MiddlewareResult<object>>;
<$ContextOverride>(opts: {
ctx?: $ContextOverride | undefined;
input?: unknown;
}): Promise<MiddlewareResult<$ContextOverride>>;
(opts: {
getRawInput: GetRawInputFn;
}): Promise<...>;
};
}): Promise<...>
isAuthed(opts: {
ctx: {
user: {
name: string;
} | null;
};
type: ProcedureType;
path: string;
input: UnsetMarker;
getRawInput: GetRawInputFn;
meta: object | undefined;
signal: AbortSignal | undefined;
batchIndex: number;
next: {
(): Promise<MiddlewareResult<object>>;
<$ContextOverride>(opts: {
ctx?: $ContextOverride | undefined;
input?: unknown;
}): Promise<MiddlewareResult<$ContextOverride>>;
(opts: {
getRawInput: GetRawInputFn;
}): Promise<...>;
};
}
opts) {
const { const ctx: {
user: {
name: string;
} | null;
}
ctx } = opts: {
ctx: {
user: {
name: string;
} | null;
};
type: ProcedureType;
path: string;
input: UnsetMarker;
getRawInput: GetRawInputFn;
meta: object | undefined;
signal: AbortSignal | undefined;
batchIndex: number;
next: {
(): Promise<MiddlewareResult<object>>;
<$ContextOverride>(opts: {
ctx?: $ContextOverride | undefined;
input?: unknown;
}): Promise<MiddlewareResult<$ContextOverride>>;
(opts: {
getRawInput: GetRawInputFn;
}): Promise<...>;
};
}
opts;
// `ctx.user` is nullable
if (!const ctx: {
user: {
name: string;
} | null;
}
ctx.user) { throw new new TRPCError(opts: {
message?: string;
code: TRPC_ERROR_CODE_KEY;
cause?: unknown;
}): TRPCError
TRPCError({ code: "UNAUTHORIZED" | "PARSE_ERROR" | "BAD_REQUEST" | "INTERNAL_SERVER_ERROR" | "NOT_IMPLEMENTED" | "BAD_GATEWAY" | "SERVICE_UNAVAILABLE" | "GATEWAY_TIMEOUT" | "PAYMENT_REQUIRED" | "FORBIDDEN" | "NOT_FOUND" | "METHOD_NOT_SUPPORTED" | "TIMEOUT" | "CONFLICT" | "PRECONDITION_FAILED" | "PAYLOAD_TOO_LARGE" | "UNSUPPORTED_MEDIA_TYPE" | "UNPROCESSABLE_CONTENT" | "PRECONDITION_REQUIRED" | "TOO_MANY_REQUESTS" | "CLIENT_CLOSED_REQUEST"code: 'UNAUTHORIZED' });
}
return opts: {
ctx: {
user: {
name: string;
} | null;
};
type: ProcedureType;
path: string;
input: UnsetMarker;
getRawInput: GetRawInputFn;
meta: object | undefined;
signal: AbortSignal | undefined;
batchIndex: number;
next: {
(): Promise<MiddlewareResult<object>>;
<$ContextOverride>(opts: {
ctx?: $ContextOverride | undefined;
input?: unknown;
}): Promise<MiddlewareResult<$ContextOverride>>;
(opts: {
getRawInput: GetRawInputFn;
}): Promise<...>;
};
}
opts.next: <{
user: {
name: string;
};
}>(opts: {
ctx?: {
user: {
name: string;
};
} | undefined;
input?: unknown;
}) => Promise<MiddlewareResult<{
user: {
name: string;
};
}>> (+2 overloads)
next({
ctx?: {
user: {
name: string;
};
} | undefined
ctx: {
// ✅ user value is known to be non-null now
user: const ctx: {
user: {
name: string;
} | null;
}
ctx.user: {
name: string;
}
user, },
});
},
);
const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder
<{
hello: QueryProcedure<{
input: string | null | undefined;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
admin: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}>(_: {
hello: QueryProcedure<{
input: string | null | undefined;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>;
admin: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>;
}) => BuiltRouter<...>
router({
// this is accessible for everyone
hello: QueryProcedure<{
input: string | null | undefined;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>
hello: const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>procedure
.ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.input<z.ZodOptional<z.ZodNullable<z.ZodString>>>(schema: z.ZodOptional<z.ZodNullable<z.ZodString>>): ProcedureBuilder<Context, object, object, string | null | undefined, string | null | undefined, UnsetMarker, UnsetMarker, false, DefaultErrorShape>input(import zz.function string(params?: string | z.core.$ZodStringParams): z.ZodString (+1 overload)string().ZodType<any, any, $ZodStringInternals<string>>.nullish(): z.ZodOptional<z.ZodNullable<z.ZodString>>nullish())
.ProcedureBuilder<Context, object, object, string | null | undefined, string | null | undefined, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<string>(resolver: ProcedureResolver<Context, object, object, string | null | undefined, UnsetMarker, string>): QueryProcedure<{
input: string | null | undefined;
output: string;
meta: object;
errorShape: DefaultErrorShape;
}>
query((opts: ProcedureResolverOptions<Context, object, object, string | null | undefined>opts) => `hello ${opts: ProcedureResolverOptions<Context, object, object, string | null | undefined>opts.ProcedureResolverOptions<Context, object, object, string | null | undefined>.input: string | null | undefinedinput ?? opts: ProcedureResolverOptions<Context, object, object, string | null | undefined>opts.ProcedureResolverOptions<Context, object, object, string | null | undefined>.ctx: {
user: {
name: string;
} | null;
}
ctx.user: {
name: string;
} | null
user?.name: string | undefinedname ?? 'world'}`),
admin: BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
admin: const t: TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, {
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<Context, object, RuntimeConfigOptions<Context, object>, { ctx: Context; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder
<{
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>(_: {
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}) => BuiltRouter<{
ctx: Context;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>;
}>>
router({
// this is accessible only to admins
secret: QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>
secret: const protectedProcedure: ProcedureBuilder<Context, object, {
user: {
name: string;
};
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
protectedProcedure.ProcedureBuilder<Context, object, { user: { name: string; }; }, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.query<{
secret: string;
}>(resolver: ProcedureResolver<Context, object, {
user: {
name: string;
};
}, UnsetMarker, UnsetMarker, {
secret: string;
}>): QueryProcedure<{
input: void;
output: {
secret: string;
};
meta: object;
errorShape: DefaultErrorShape;
}>
query((opts: ProcedureResolverOptions<Context, object, {
user: {
name: string;
};
}, UnsetMarker>
opts) => {
return {
secret: stringsecret: 'sauce',
};
}),
}),
});