Saltar al contenido principal
Versión: 11.x

Contexto

Traducción Beta No Oficial

Esta página fue traducida por PageTurner AI (beta). No está respaldada oficialmente por el proyecto. ¿Encontraste un error? Reportar problema →

Tu contexto contiene datos a los que todos tus procedimientos tRPC tendrán acceso, siendo un lugar ideal para conexiones a bases de datos o información de autenticación.

Configurar el contexto se realiza en 2 pasos: definir el tipo durante la inicialización y luego crear el contexto en tiempo de ejecución para cada solicitud.

Definir el tipo de contexto

Al inicializar tRPC con initTRPC, debes encadenar .context<TContext>() a la función constructora initTRPC antes de llamar a .create(). El tipo TContext puede inferirse del tipo de retorno de una función o definirse explícitamente.

Esto garantizará que tu contexto tenga tipado correcto en procedimientos y middlewares.

ts
import { initTRPC } from '@trpc/server';
import type { CreateNextContextOptions } from '@trpc/server/adapters/next';
import { getSession } from 'next-auth/react';
 
export const createContext = async (opts: CreateNextContextOptions) => {
const session = await getSession({ req: opts.req });
 
return {
session,
};
};
 
export type Context = Awaited<ReturnType<typeof createContext>>;
const t = initTRPC.context<Context>().create();
 
t.procedure.use((opts) => {
opts.ctx;
(property) ctx: { session: Session | null; }
 
return opts.next();
});
ts
import { initTRPC } from '@trpc/server';
import type { CreateNextContextOptions } from '@trpc/server/adapters/next';
import { getSession } from 'next-auth/react';
 
export const createContext = async (opts: CreateNextContextOptions) => {
const session = await getSession({ req: opts.req });
 
return {
session,
};
};
 
export type Context = Awaited<ReturnType<typeof createContext>>;
const t = initTRPC.context<Context>().create();
 
t.procedure.use((opts) => {
opts.ctx;
(property) ctx: { session: Session | null; }
 
return opts.next();
});

Crear el contexto

La función createContext() debe pasarse al manejador que monta tu appRouter, ya sea mediante HTTP, una llamada de servidor o nuestros servicios de servidor.

createContext() se llama en cada invocación de tRPC, por lo que las solicitudes agrupadas compartirán un contexto.

ts
// 1. HTTP request
import { createHTTPHandler } from '@trpc/server/adapters/standalone';
import { createContext } from './context';
import { appRouter } from './router';
const handler = createHTTPHandler({
router: appRouter,
createContext,
});
ts
// 1. HTTP request
import { createHTTPHandler } from '@trpc/server/adapters/standalone';
import { createContext } from './context';
import { appRouter } from './router';
const handler = createHTTPHandler({
router: appRouter,
createContext,
});
ts
// 2. Server-side call
import { createContext } from './context';
import { createCaller } from './router';
const caller = createCaller(await createContext());
ts
// 2. Server-side call
import { createContext } from './context';
import { createCaller } from './router';
const caller = createCaller(await createContext());
ts
// 3. servers-side helpers
import { createServerSideHelpers } from '@trpc/react-query/server';
import { createContext } from './context';
import { appRouter } from './router';
const helpers = createServerSideHelpers({
router: appRouter,
ctx: await createContext(),
});
ts
// 3. servers-side helpers
import { createServerSideHelpers } from '@trpc/react-query/server';
import { createContext } from './context';
import { appRouter } from './router';
const helpers = createServerSideHelpers({
router: appRouter,
ctx: await createContext(),
});

Código de ejemplo

tsx
// -------------------------------------------------
// @filename: context.ts
// -------------------------------------------------
import type { CreateNextContextOptions } from '@trpc/server/adapters/next';
import { getSession } from 'next-auth/react';
 
/**
* Creates context for an incoming request
* @see https://trpc.io/docs/v11/context
*/
export async function createContext(opts: CreateNextContextOptions) {
const session = await getSession({ req: opts.req });
 
return {
session,
};
}
 
export type Context = Awaited<ReturnType<typeof createContext>>;
 
// -------------------------------------------------
// @filename: trpc.ts
// -------------------------------------------------
import { initTRPC, TRPCError } from '@trpc/server';
import { Context } from './context';
 
const t = initTRPC.context<Context>().create();
 
 
export const router = t.router;
 
/**
* Unprotected procedure
*/
export const publicProcedure = t.procedure;
 
/**
* Protected procedure
*/
export const protectedProcedure = t.procedure.use(function isAuthed(opts) {
if (!opts.ctx.session?.user?.email) {
throw new TRPCError({
code: 'UNAUTHORIZED',
});
}
return opts.next({
ctx: {
// Infers the `session` as non-nullable
session: opts.ctx.session,
},
});
});
tsx
// -------------------------------------------------
// @filename: context.ts
// -------------------------------------------------
import type { CreateNextContextOptions } from '@trpc/server/adapters/next';
import { getSession } from 'next-auth/react';
 
/**
* Creates context for an incoming request
* @see https://trpc.io/docs/v11/context
*/
export async function createContext(opts: CreateNextContextOptions) {
const session = await getSession({ req: opts.req });
 
return {
session,
};
}
 
export type Context = Awaited<ReturnType<typeof createContext>>;
 
// -------------------------------------------------
// @filename: trpc.ts
// -------------------------------------------------
import { initTRPC, TRPCError } from '@trpc/server';
import { Context } from './context';
 
const t = initTRPC.context<Context>().create();
 
 
export const router = t.router;
 
/**
* Unprotected procedure
*/
export const publicProcedure = t.procedure;
 
/**
* Protected procedure
*/
export const protectedProcedure = t.procedure.use(function isAuthed(opts) {
if (!opts.ctx.session?.user?.email) {
throw new TRPCError({
code: 'UNAUTHORIZED',
});
}
return opts.next({
ctx: {
// Infers the `session` as non-nullable
session: opts.ctx.session,
},
});
});

Contexto interno y externo

En algunos escenarios puede ser útil dividir tu contexto en funciones "internas" y "externas".

Contexto interno: Aquí defines elementos que no dependen de la solicitud, como conexiones a bases de datos. Puedes usar esta función para pruebas de integración o servicios de servidor donde no tienes un objeto de solicitud. Todo lo definido aquí estará siempre disponible en tus procedimientos.

Contexto externo: Aquí defines elementos que dependen de la solicitud, como la sesión del usuario. Lo definido aquí solo está disponible para procedimientos llamados mediante HTTP.

Ejemplo de contexto interno y externo

ts
import type { CreateNextContextOptions } from '@trpc/server/adapters/next';
import { getSessionFromCookie, type Session } from './auth';
/**
* Defines your inner context shape.
* Add fields here that the inner context brings.
*/
interface CreateInnerContextOptions extends Partial<CreateNextContextOptions> {
session: 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 Next.js' `req`/`res`
* - tRPC's `createServerSideHelpers` where we don't have `req`/`res`
*
* @see https://trpc.io/docs/v11/context#inner-and-outer-context
*/
export async function createContextInner(opts?: CreateInnerContextOptions) {
return {
prisma,
session: opts.session,
};
}
/**
* 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 createContext(opts: CreateNextContextOptions) {
const session = getSessionFromCookie(opts.req);
const contextInner = await createContextInner({ session });
return {
...contextInner,
req: opts.req,
res: opts.res,
};
}
export type Context = Awaited<ReturnType<typeof createContextInner>>;
// The usage in your router is the same as the example above.
ts
import type { CreateNextContextOptions } from '@trpc/server/adapters/next';
import { getSessionFromCookie, type Session } from './auth';
/**
* Defines your inner context shape.
* Add fields here that the inner context brings.
*/
interface CreateInnerContextOptions extends Partial<CreateNextContextOptions> {
session: 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 Next.js' `req`/`res`
* - tRPC's `createServerSideHelpers` where we don't have `req`/`res`
*
* @see https://trpc.io/docs/v11/context#inner-and-outer-context
*/
export async function createContextInner(opts?: CreateInnerContextOptions) {
return {
prisma,
session: opts.session,
};
}
/**
* 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 createContext(opts: CreateNextContextOptions) {
const session = getSessionFromCookie(opts.req);
const contextInner = await createContextInner({ session });
return {
...contextInner,
req: opts.req,
res: opts.res,
};
}
export type Context = Awaited<ReturnType<typeof createContextInner>>;
// The usage in your router is the same as the example above.

Es crucial inferir tu Context desde el contexto interno, pues solo lo definido allí está realmente disponible siempre en tus procedimientos.

Si prefieres no verificar constantemente si req o res son undefined en tus procedimientos, puedes crear un pequeño procedimiento reutilizable:

ts
export const apiProcedure = publicProcedure.use((opts) => {
if (!opts.ctx.req || !opts.ctx.res) {
throw new Error('You are missing `req` or `res` in your call.');
}
return opts.next({
ctx: {
// We overwrite the context with the truthy `req` & `res`, which will also overwrite the types used in your procedure.
req: opts.ctx.req,
res: opts.ctx.res,
},
});
});
ts
export const apiProcedure = publicProcedure.use((opts) => {
if (!opts.ctx.req || !opts.ctx.res) {
throw new Error('You are missing `req` or `res` in your call.');
}
return opts.next({
ctx: {
// We overwrite the context with the truthy `req` & `res`, which will also overwrite the types used in your procedure.
req: opts.ctx.req,
res: opts.ctx.res,
},
});
});

Limitar el tamaño de lotes

Puedes usar el contexto para limitar la cantidad de solicitudes que pueden agruparse.

ts
import { TRPCError } from '@trpc/server';
import type { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
 
const MAX_BATCH_SIZE = 10;
 
// Create a context that checks batch size
export async function createContext(opts: CreateHTTPContextOptions) {
if (opts.info.calls.length > MAX_BATCH_SIZE) {
throw new TRPCError({
code: 'TOO_MANY_REQUESTS',
message: `Batch size limit of ${MAX_BATCH_SIZE} exceeded`,
});
}
return {};
}
ts
import { TRPCError } from '@trpc/server';
import type { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
 
const MAX_BATCH_SIZE = 10;
 
// Create a context that checks batch size
export async function createContext(opts: CreateHTTPContextOptions) {
if (opts.info.calls.length > MAX_BATCH_SIZE) {
throw new TRPCError({
code: 'TOO_MANY_REQUESTS',
message: `Batch size limit of ${MAX_BATCH_SIZE} exceeded`,
});
}
return {};
}

Este contexto lanzará un error TOO_MANY_REQUESTS si un cliente intenta agrupar más de 10 solicitudes. Puedes ajustar la constante MAX_BATCH_SIZE según tus necesidades.