Context
On this page
Your context holds data that all of your tRPC procedures will have access to, and is a great place to put things like authentication information.
Setting up the context is done in 2 steps, defining the type during initialization and then creating the runtime context for each request.
Defining the context type
When initializing tRPC using initTRPC, you should pipe .context<TContext>() to the initTRPC builder function before calling .create(). The type TContext can either be inferred from a function's return type or be explicitly defined.
This will make sure your context is properly typed in your procedures and middlewares.
import { const initTRPC: trpc.TRPCBuilder<object, object>initTRPC } from '@trpc/server';
import type { type CreateHTTPContextOptions = {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
info: TRPCRequestInfo;
}
CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
export const const createContext: (opts: CreateHTTPContextOptions) => Promise<{
token: string | undefined;
}>
createContext = async (opts: CreateHTTPContextOptionsopts: type CreateHTTPContextOptions = {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
info: TRPCRequestInfo;
}
CreateHTTPContextOptions) => {
// Example: extract a session token from the request headers
const const token: string | undefinedtoken = opts: CreateHTTPContextOptionsopts.req: IncomingMessagereq.IncomingMessage.headers: IncomingHttpHeadersheaders['authorization'];
return {
token: string | undefinedtoken,
};
};
export type type Context = {
token: string | undefined;
}
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 const createContext: (opts: CreateHTTPContextOptions) => Promise<{
token: string | undefined;
}>
createContext>>;
const const t: trpc.TRPCRootObject<{
token: string | undefined;
}, object, trpc.TRPCRuntimeConfigOptions<{
token: string | undefined;
}, object>, {
ctx: {
token: string | undefined;
};
meta: object;
errorShape: trpc.TRPCDefaultErrorShape;
transformer: false;
}>
t = const initTRPC: trpc.TRPCBuilder<object, object>initTRPC.TRPCBuilder<object, object>.context<{
token: string | undefined;
}>(): trpc.TRPCBuilder<{
token: string | undefined;
}, object>
context<type Context = {
token: string | undefined;
}
Context>().TRPCBuilder<{ token: string | undefined; }, object>.create<trpc.TRPCRuntimeConfigOptions<{
token: string | undefined;
}, object>>(opts?: trpc.TRPCRuntimeConfigOptions<{
token: string | undefined;
}, object> | undefined): trpc.TRPCRootObject<{
token: string | undefined;
}, object, trpc.TRPCRuntimeConfigOptions<{
token: string | undefined;
}, object>, {
ctx: {
token: string | undefined;
};
meta: object;
errorShape: trpc.TRPCDefaultErrorShape;
transformer: false;
}>
create();
const t: trpc.TRPCRootObject<{
token: string | undefined;
}, object, trpc.TRPCRuntimeConfigOptions<{
token: string | undefined;
}, object>, {
ctx: {
token: string | undefined;
};
meta: object;
errorShape: trpc.TRPCDefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<{ token: string | undefined; }, object, RuntimeConfigOptions<{ token: string | undefined; }, object>, { ctx: { token: string | undefined; }; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: trpc.TRPCProcedureBuilder<{
token: string | undefined;
}, object, object, trpc.TRPCUnsetMarker, trpc.TRPCUnsetMarker, trpc.TRPCUnsetMarker, trpc.TRPCUnsetMarker, false, trpc.TRPCDefaultErrorShape>
procedure.ProcedureBuilder<{ token: string | undefined; }, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.use<object>(fn: trpc.TRPCMiddlewareBuilder<{
token: string | undefined;
}, object, object, trpc.TRPCUnsetMarker> | trpc.TRPCMiddlewareFunction<{
token: string | undefined;
}, object, object, object, trpc.TRPCUnsetMarker>): trpc.TRPCProcedureBuilder<{
token: string | undefined;
}, object, {}, trpc.TRPCUnsetMarker, trpc.TRPCUnsetMarker, trpc.TRPCUnsetMarker, trpc.TRPCUnsetMarker, false, trpc.TRPCDefaultErrorShape>
use((opts: {
ctx: {
token: string | undefined;
};
type: trpc.ProcedureType;
path: string;
input: trpc.TRPCUnsetMarker;
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) => {
opts: {
ctx: {
token: string | undefined;
};
type: trpc.ProcedureType;
path: string;
input: trpc.TRPCUnsetMarker;
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;
return opts: {
ctx: {
token: string | undefined;
};
type: trpc.ProcedureType;
path: string;
input: trpc.TRPCUnsetMarker;
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: () => Promise<MiddlewareResult<object>> (+2 overloads)next();
});Creating the context
The createContext() function must be passed to the handler mounting your appRouter. The handler may use HTTP or a server-side call.
createContext() is called once per request, so all procedures within a single batched request share the same context.
// 1. HTTP request
import { function createHTTPHandler<TRouter extends AnyRouter>(opts: CreateHTTPHandlerOptions<TRouter>): RequestListenercreateHTTPHandler } from '@trpc/server/adapters/standalone';
import { function createContext(opts: CreateHTTPContextOptions): Promise<{
token: string | undefined;
}>
createContext } from './context';
import { const appRouter: BuiltRouter<{
ctx: {
token: string | undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
appRouter } from './router';
const const handler: RequestListener<typeof IncomingMessage, typeof ServerResponse>handler = createHTTPHandler<BuiltRouter<{
ctx: {
token: string | undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: CreateHTTPHandlerOptions<BuiltRouter<{
ctx: {
token: string | undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>): RequestListener
createHTTPHandler({
BaseHandlerOptions<BuiltRouter<{ ctx: { token: string | undefined; }; meta: object; errorShape: DefaultErrorShape; transformer: false; }, DecorateCreateRouterOptions<{}>>, IncomingMessage>.router: BuiltRouter<{
ctx: {
token: string | undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
router: const appRouter: BuiltRouter<{
ctx: {
token: string | undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
appRouter,
createContext: NodeHTTPCreateContextFn<BuiltRouter<{
ctx: {
token: string | undefined;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>, IncomingMessage, ServerResponse<IncomingMessage>>
createContext,
});// 2. Server-side call
import { function createContext(): Promise<{
token: string;
}>
createContext } from './context';
import { const createCaller: RouterCaller<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
createCaller } from './router';
const const caller: DecorateRouterRecord<DecorateCreateRouterOptions<{}>>caller = function createCaller(ctx: object | (() => MaybePromise<object>), options?: {
onError?: RouterCallerErrorHandler<object> | undefined;
signal?: AbortSignal;
} | undefined): DecorateRouterRecord<DecorateCreateRouterOptions<{}>>
createCaller(await function createContext(): Promise<{
token: string;
}>
createContext());// 3. Server-side helpers (Next.js-specific, see /docs/client/nextjs/pages-router/server-side-helpers)
import { function createServerSideHelpers<TRouter extends AnyRouter>(opts: CreateServerSideHelpersOptions<TRouter>): ProtectedIntersection<{
queryClient: QueryClient;
dehydrate: (opts?: DehydrateOptions) => DehydratedState;
}, DecoratedProcedureSSGRecord<TRouter["_def"]["_config"]["$types"], TRouter["_def"]["record"]>>
createServerSideHelpers } from '@trpc/react-query/server';
import { function createContext(): Promise<{}>createContext } from './context';
import { const appRouter: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
appRouter } from './router';
const const helpers: {
queryClient: QueryClient;
dehydrate: (opts?: DehydrateOptions) => DehydratedState;
} & DecoratedProcedureSSGRecord<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
helpers = createServerSideHelpers<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>(opts: CreateServerSideHelpersOptions<BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>>): {
queryClient: QueryClient;
dehydrate: (opts?: DehydrateOptions) => DehydratedState;
} & DecoratedProcedureSSGRecord<...>
createServerSideHelpers({
router: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
router: const appRouter: BuiltRouter<{
ctx: object;
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}, DecorateCreateRouterOptions<{}>>
appRouter,
ctx: objectctx: await function createContext(): Promise<{}>createContext(),
});Example code
// -------------------------------------------------
// @filename: src/context.ts
// -------------------------------------------------
import type { type CreateHTTPContextOptions = {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
info: TRPCRequestInfo;
}
CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
/**
* Creates context for an incoming request
* @see https://trpc.io/docs/v11/context
*/
export async function function createContext(opts: CreateHTTPContextOptions): Promise<{
user: {
email: string;
} | null;
}>
createContext(opts: CreateHTTPContextOptionsopts: type CreateHTTPContextOptions = {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
info: TRPCRequestInfo;
}
CreateHTTPContextOptions) {
const const token: string | undefinedtoken = opts: CreateHTTPContextOptionsopts.req: IncomingMessagereq.IncomingMessage.headers: IncomingHttpHeadersheaders['authorization'];
// In a real app, you would verify the token and look up the user
const const user: {
email: string;
} | null
user = const token: string | undefinedtoken ? { email: stringemail: 'user@example.com' } : null;
return {
user: {
email: string;
} | null
user,
};
}
export type type Context = {
user: {
email: 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(opts: CreateHTTPContextOptions): Promise<{
user: {
email: string;
} | null;
}>
createContext>>;
// -------------------------------------------------
// @filename: src/trpc.ts
// -------------------------------------------------
import { const initTRPC: TRPCBuilder<object, object>initTRPC, class TRPCErrorTRPCError } from '@trpc/server';
import { type Context = {
user: {
email: string;
} | null;
}
Context } from './context';
const const t: TRPCRootObject<{
user: {
email: string;
} | null;
}, object, RuntimeConfigOptions<{
user: {
email: string;
} | null;
}, object>, {
ctx: {
user: {
email: string;
} | null;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t = const initTRPC: TRPCBuilder<object, object>initTRPC.TRPCBuilder<object, object>.context<{
user: {
email: string;
} | null;
}>(): TRPCBuilder<{
user: {
email: string;
} | null;
}, object>
context<type Context = {
user: {
email: string;
} | null;
}
Context>().TRPCBuilder<{ user: { email: string; } | null; }, object>.create<RuntimeConfigOptions<{
user: {
email: string;
} | null;
}, object>>(opts?: RuntimeConfigOptions<{
user: {
email: string;
} | null;
}, object> | undefined): TRPCRootObject<{
user: {
email: string;
} | null;
}, object, RuntimeConfigOptions<{
user: {
email: string;
} | null;
}, object>, {
ctx: {
user: {
email: string;
} | null;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
create();
export const const router: RouterBuilder<{
ctx: {
user: {
email: string;
} | null;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
router = const t: TRPCRootObject<{
user: {
email: string;
} | null;
}, object, RuntimeConfigOptions<{
user: {
email: string;
} | null;
}, object>, {
ctx: {
user: {
email: string;
} | null;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<{ user: { email: string; } | null; }, object, RuntimeConfigOptions<{ user: { email: string; } | null; }, object>, { ctx: { user: { email: string; } | null; }; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.router: RouterBuilder<{
ctx: {
user: {
email: string;
} | null;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
router;
/**
* Unprotected procedure
*/
export const const publicProcedure: ProcedureBuilder<{
user: {
email: string;
} | null;
}, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
publicProcedure = const t: TRPCRootObject<{
user: {
email: string;
} | null;
}, object, RuntimeConfigOptions<{
user: {
email: string;
} | null;
}, object>, {
ctx: {
user: {
email: string;
} | null;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<{ user: { email: string; } | null; }, object, RuntimeConfigOptions<{ user: { email: string; } | null; }, object>, { ctx: { user: { email: string; } | null; }; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<{
user: {
email: string;
} | null;
}, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
procedure;
/**
* Protected procedure
*/
export const const protectedProcedure: ProcedureBuilder<{
user: {
email: string;
} | null;
}, object, {
user: {
email: string;
};
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
protectedProcedure = const t: TRPCRootObject<{
user: {
email: string;
} | null;
}, object, RuntimeConfigOptions<{
user: {
email: string;
} | null;
}, object>, {
ctx: {
user: {
email: string;
} | null;
};
meta: object;
errorShape: DefaultErrorShape;
transformer: false;
}>
t.TRPCRootObject<{ user: { email: string; } | null; }, object, RuntimeConfigOptions<{ user: { email: string; } | null; }, object>, { ctx: { user: { email: string; } | null; }; meta: object; errorShape: DefaultErrorShape; transformer: false; }>.procedure: ProcedureBuilder<{
user: {
email: string;
} | null;
}, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
procedure.ProcedureBuilder<{ user: { email: string; } | null; }, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.use<{
user: {
email: string;
};
}>(fn: MiddlewareBuilder<{
user: {
email: string;
} | null;
}, object, {
user: {
email: string;
};
}, UnsetMarker> | MiddlewareFunction<{
user: {
email: string;
} | null;
}, object, object, {
user: {
email: string;
};
}, UnsetMarker>): ProcedureBuilder<{
user: {
email: string;
} | null;
}, object, {
user: {
email: string;
};
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
use(function function (local function) isAuthed(opts: {
ctx: {
user: {
email: 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: {
email: 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) {
if (!opts: {
ctx: {
user: {
email: 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: {
email: string;
} | null;
}
ctx.user: {
email: string;
} | null
user?.email: string | undefinedemail) {
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: {
email: 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: {
email: string;
};
}>(opts: {
ctx?: {
user: {
email: string;
};
} | undefined;
input?: unknown;
}) => Promise<MiddlewareResult<{
user: {
email: string;
};
}>> (+2 overloads)
next({
ctx?: {
user: {
email: string;
};
} | undefined
ctx: {
// Infers the `user` as non-nullable
user: {
email: string;
}
user: opts: {
ctx: {
user: {
email: 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: {
email: string;
} | null;
}
ctx.user: {
email: string;
}
user,
},
});
});Inner and outer context
In some scenarios it could make sense to split up your context into "inner" and "outer" functions.
Inner context is where you define context which doesn’t depend on the request, e.g. your database connection. You can use this function for integration testing or server-side calls, where you don’t have a request object. Whatever is defined here will always be available in your procedures.
Outer context is where you define context which depends on the request, e.g. for the user's session. Whatever is defined here is only available for procedures that are called via HTTP.
Example for inner & outer context
import type { type CreateHTTPContextOptions = {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
info: TRPCRequestInfo;
}
CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
import { function getSessionFromCookie(req: IncomingMessage): Session | nullgetSessionFromCookie, type type Session = {
user: {
email: string;
};
}
Session } from './auth';
import { const db: {}db } from './db';
/**
* Defines your inner context shape.
* Add fields here that the inner context brings.
*/
interface CreateInnerContextOptions {
CreateInnerContextOptions.session: Session | nullsession: type Session = {
user: {
email: string;
};
}
Session | null;
}
/**
* Inner context. Will always be available in your procedures, in contrast to the outer context.
*
* Also useful for:
* - testing, so you don't have to mock `req`/`res`
* - server-side calls where we don't have `req`/`res`
*
* @see https://trpc.io/docs/v11/context#inner-and-outer-context
*/
export async function function createContextInner(opts?: CreateInnerContextOptions): Promise<{
db: {};
session: Session | null | undefined;
}>
createContextInner(opts: CreateInnerContextOptions | undefinedopts?: CreateInnerContextOptions) {
return {
db: {}db,
session: Session | null | undefinedsession: opts: CreateInnerContextOptions | undefinedopts?.CreateInnerContextOptions.session: Session | null | undefinedsession,
};
}
/**
* Outer context. Used in the routers and will e.g. bring `req` & `res` to the context as "not `undefined`".
*
* @see https://trpc.io/docs/v11/context#inner-and-outer-context
*/
export async function function createContext(opts: CreateHTTPContextOptions): Promise<{
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
db: {};
session: Session | null | undefined;
}>
createContext(opts: CreateHTTPContextOptionsopts: type CreateHTTPContextOptions = {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
info: TRPCRequestInfo;
}
CreateHTTPContextOptions) {
const const session: Session | nullsession = function getSessionFromCookie(req: IncomingMessage): Session | nullgetSessionFromCookie(opts: CreateHTTPContextOptionsopts.req: IncomingMessagereq);
const const contextInner: {
db: {};
session: Session | null | undefined;
}
contextInner = await function createContextInner(opts?: CreateInnerContextOptions): Promise<{
db: {};
session: Session | null | undefined;
}>
createContextInner({ CreateInnerContextOptions.session: Session | nullsession });
return {
...const contextInner: {
db: {};
session: Session | null | undefined;
}
contextInner,
req: IncomingMessagereq: opts: CreateHTTPContextOptionsopts.req: IncomingMessagereq,
res: ServerResponse<IncomingMessage>res: opts: CreateHTTPContextOptionsopts.res: ServerResponse<IncomingMessage>res,
};
}
export type type Context = {
db: {};
session: Session | null | undefined;
}
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 createContextInner(opts?: CreateInnerContextOptions): Promise<{
db: {};
session: Session | null | undefined;
}>
createContextInner>>;
// The usage in your router is the same as the example above.It is important to infer your Context from the inner context, as only what is defined there is really always available in your procedures.
If you don't want to check req or res for undefined in your procedures all the time, you could build a small reusable procedure for that:
export const const apiProcedure: ProcedureBuilder<Context, object, {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
}, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>
apiProcedure = const publicProcedure: ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>publicProcedure.ProcedureBuilder<Context, object, object, UnsetMarker, UnsetMarker, UnsetMarker, UnsetMarker, false, DefaultErrorShape>.use<{
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
}>(fn: MiddlewareBuilder<{
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
}, object, {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
}, UnsetMarker> | MiddlewareFunction<Context, object, object, {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
}, UnsetMarker>): ProcedureBuilder<...>
use((opts: {
ctx: {
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
};
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) => {
if (!opts: {
ctx: {
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
};
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: {
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
}
ctx.req: IncomingMessage | undefinedreq || !opts: {
ctx: {
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
};
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: {
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
}
ctx.res: ServerResponse<IncomingMessage> | undefinedres) {
throw new var Error: ErrorConstructor
new (message?: string, options?: ErrorOptions) => Error (+1 overload)
Error('You are missing `req` or `res` in your call.');
}
return opts: {
ctx: {
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
};
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: <{
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
}>(opts: {
ctx?: {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
} | undefined;
input?: unknown;
}) => Promise<MiddlewareResult<{
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
}>> (+2 overloads)
next({
ctx?: {
req: IncomingMessage;
res: ServerResponse<IncomingMessage>;
} | undefined
ctx: {
// We overwrite the context with the truthy `req` & `res`, which will also overwrite the types used in your procedure.
req: IncomingMessagereq: opts: {
ctx: {
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
};
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: {
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
}
ctx.req: IncomingMessagereq,
res: ServerResponse<IncomingMessage>res: opts: {
ctx: {
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
};
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: {
req: IncomingMessage | undefined;
res: ServerResponse<IncomingMessage> | undefined;
}
ctx.res: ServerResponse<IncomingMessage>res,
},
});
});