Aller au contenu principal
Version : 11.x

Contexte

Traduction Bêta Non Officielle

Cette page a été traduite par PageTurner AI (bêta). Non approuvée officiellement par le projet. Vous avez trouvé une erreur ? Signaler un problème →

Votre contexte contient des données accessibles par toutes vos procédures tRPC, et constitue l'endroit idéal pour placer des éléments tels que les informations d'authentification.

La configuration du contexte se fait en 2 étapes : définir le type lors de l'initialisation, puis créer le contexte d'exécution pour chaque requête.

Définition du type de contexte

Lors de l'initialisation de tRPC avec initTRPC, vous devez chaîner .context<TContext>() à la fonction de construction initTRPC avant d'appeler .create(). Le type TContext peut être inféré à partir du type de retour d'une fonction ou être explicitement défini.

Cela garantira que votre contexte soit correctement typé dans vos procédures et middlewares.

ts
import { initTRPC } from '@trpc/server';
import type { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
 
export const createContext = async (opts: CreateHTTPContextOptions) => {
// Example: extract a session token from the request headers
const token = opts.req.headers['authorization'];
 
return {
token,
};
};
 
export type Context = Awaited<ReturnType<typeof createContext>>;
const t = initTRPC.context<Context>().create();
 
t.procedure.use((opts) => {
opts.ctx;
(property) ctx: { token: any; }
 
return opts.next();
});
ts
import { initTRPC } from '@trpc/server';
import type { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
 
export const createContext = async (opts: CreateHTTPContextOptions) => {
// Example: extract a session token from the request headers
const token = opts.req.headers['authorization'];
 
return {
token,
};
};
 
export type Context = Awaited<ReturnType<typeof createContext>>;
const t = initTRPC.context<Context>().create();
 
t.procedure.use((opts) => {
opts.ctx;
(property) ctx: { token: any; }
 
return opts.next();
});

Création du contexte

La fonction createContext() doit être transmise au gestionnaire qui monte votre appRouter. Ce gestionnaire peut utiliser HTTP ou un appel côté serveur.

createContext() est appelée une fois par requête, donc toutes les procédures d'une même requête groupée partagent le même contexte.

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. Server-side helpers (Next.js-specific, see /docs/client/nextjs/pages-router/server-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. Server-side helpers (Next.js-specific, see /docs/client/nextjs/pages-router/server-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(),
});

Exemple de code

ts
// -------------------------------------------------
// @filename: context.ts
// -------------------------------------------------
import type { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
 
/**
* Creates context for an incoming request
* @see https://trpc.io/docs/v11/context
*/
export async function createContext(opts: CreateHTTPContextOptions) {
const token = opts.req.headers['authorization'];
 
// In a real app, you would verify the token and look up the user
const user = token ? { email: 'user@example.com' } : null;
 
return {
user,
};
}
 
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.user?.email) {
throw new TRPCError({
code: 'UNAUTHORIZED',
});
}
return opts.next({
ctx: {
// Infers the `user` as non-nullable
user: opts.ctx.user,
},
});
});
ts
// -------------------------------------------------
// @filename: context.ts
// -------------------------------------------------
import type { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
 
/**
* Creates context for an incoming request
* @see https://trpc.io/docs/v11/context
*/
export async function createContext(opts: CreateHTTPContextOptions) {
const token = opts.req.headers['authorization'];
 
// In a real app, you would verify the token and look up the user
const user = token ? { email: 'user@example.com' } : null;
 
return {
user,
};
}
 
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.user?.email) {
throw new TRPCError({
code: 'UNAUTHORIZED',
});
}
return opts.next({
ctx: {
// Infers the `user` as non-nullable
user: opts.ctx.user,
},
});
});

Contexte interne et externe

Dans certains scénarios, il peut être judicieux de scinder votre contexte en fonctions "interne" et "externe".

Le contexte interne est l'endroit où vous définissez les éléments indépendants de la requête, comme votre connexion à la base de données. Vous pouvez utiliser cette fonction pour les tests d'intégration ou les appels côté serveur, lorsque vous n'avez pas d'objet requête. Tout ce qui est défini ici sera toujours disponible dans vos procédures.

Compromis pour les gros clients dans createContextInner

Placer un client de base de données comme prisma dans createContextInner est pratique et courant, mais les gros clients générés (comme Prisma) peuvent augmenter la surcharge de vérification de types car ils deviennent partie intégrante de votre type de contexte dans toutes les procédures.

Si cette surcharge devient perceptible, une alternative consiste à garder le contexte plus petit et à importer le client directement aux endroits où il est nécessaire.

Le contexte externe définit les éléments dépendants de la requête, comme la session utilisateur. Ce qui est défini ici n'est disponible que pour les procédures appelées via HTTP.

Exemple de contexte interne et externe

ts
import type { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
import { getSessionFromCookie, type Session } from './auth';
import { db } from './db';
 
/**
* Defines your inner context shape.
* Add fields here that the inner context brings.
*/
interface CreateInnerContextOptions {
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 `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 createContextInner(opts?: CreateInnerContextOptions) {
return {
db,
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: CreateHTTPContextOptions) {
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 { CreateHTTPContextOptions } from '@trpc/server/adapters/standalone';
import { getSessionFromCookie, type Session } from './auth';
import { db } from './db';
 
/**
* Defines your inner context shape.
* Add fields here that the inner context brings.
*/
interface CreateInnerContextOptions {
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 `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 createContextInner(opts?: CreateInnerContextOptions) {
return {
db,
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: CreateHTTPContextOptions) {
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.

Il est crucial d'inférer votre Context à partir du contexte interne, car seul ce qui y est défini est véritablement toujours disponible dans vos procédures.

Si vous souhaitez éviter de vérifier constamment si req ou res sont undefined dans vos procédures, vous pouvez créer une petite procédure réutilisable :

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,
},
});
});