Hoppa till huvudinnehållet
Version: 11.x

Middlewares

Inofficiell Beta-översättning

Denna sida har översatts av PageTurner AI (beta). Inte officiellt godkänd av projektet. Hittade du ett fel? Rapportera problem →

Du kan lägga till middleware(s) till en procedur med metoden t.procedure.use(). Middleware(s) kommer att omsluta proceduranropet och måste skicka vidare dess returvärde.

Auktorisering

I exemplet nedan kommer alla anrop till adminProcedure att säkerställa att användaren är "admin" innan exekvering.

ts
import { TRPCError, initTRPC } from '@trpc/server';
 
interface Context {
user?: {
id: string;
isAdmin: boolean;
// [..]
};
}
 
const t = initTRPC.context<Context>().create();
export const publicProcedure = t.procedure;
export const router = t.router;
 
export const adminProcedure = publicProcedure.use(async (opts) => {
const { ctx } = opts;
if (!ctx.user?.isAdmin) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return opts.next({
ctx: {
user: ctx.user,
},
});
});
ts
import { TRPCError, initTRPC } from '@trpc/server';
 
interface Context {
user?: {
id: string;
isAdmin: boolean;
// [..]
};
}
 
const t = initTRPC.context<Context>().create();
export const publicProcedure = t.procedure;
export const router = t.router;
 
export const adminProcedure = publicProcedure.use(async (opts) => {
const { ctx } = opts;
if (!ctx.user?.isAdmin) {
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
return opts.next({
ctx: {
user: ctx.user,
},
});
});
ts
import { adminProcedure, publicProcedure, router } from './trpc';
 
const adminRouter = router({
secretPlace: adminProcedure.query(() => 'a key'),
});
 
export const appRouter = router({
foo: publicProcedure.query(() => 'bar'),
admin: adminRouter,
});
ts
import { adminProcedure, publicProcedure, router } from './trpc';
 
const adminRouter = router({
secretPlace: adminProcedure.query(() => 'a key'),
});
 
export const appRouter = router({
foo: publicProcedure.query(() => 'bar'),
admin: adminRouter,
});
tips

Se Felhantering för att lära dig mer om TRPCError som kastas i exemplet ovan.

Loggning

I exemplet nedan loggas tidsåtgång för frågor automatiskt.

ts
export const loggedProcedure = publicProcedure.use(async (opts) => {
const start = Date.now();
 
const result = await opts.next();
 
const durationMs = Date.now() - start;
const meta = { path: opts.path, type: opts.type, durationMs };
 
result.ok
? console.log('OK request timing:', meta)
: console.error('Non-OK request timing', meta);
 
return result;
});
ts
export const loggedProcedure = publicProcedure.use(async (opts) => {
const start = Date.now();
 
const result = await opts.next();
 
const durationMs = Date.now() - start;
const meta = { path: opts.path, type: opts.type, durationMs };
 
result.ok
? console.log('OK request timing:', meta)
: console.error('Non-OK request timing', meta);
 
return result;
});
ts
import { loggedProcedure, router } from './trpc';
 
export const appRouter = router({
foo: loggedProcedure.query(() => 'bar'),
abc: loggedProcedure.query(() => 'def'),
});
ts
import { loggedProcedure, router } from './trpc';
 
export const appRouter = router({
foo: loggedProcedure.query(() => 'bar'),
abc: loggedProcedure.query(() => 'def'),
});

Kontextutökning

"Kontextutökning" gör det möjligt för middlewares att dynamiskt lägga till och åsidosätta nycklar i en basprocedurs kontext på ett typsäkert sätt.

Nedan visas ett exempel på en middleware som ändrar egenskaper i en kontext. Ändringarna blir sedan tillgängliga för alla kedjade konsumenter, som andra middlewares och procedurer:

ts
type Context = {
// user is nullable
user?: {
id: string;
};
};
 
const protectedProcedure = publicProcedure.use(async function isAuthed(opts) {
const { ctx } = opts;
// `ctx.user` is nullable
if (!ctx.user) {
(property) user: { id: string; } | undefined
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
 
return opts.next({
ctx: {
// ✅ user value is known to be non-null now
user: ctx.user,
(property) user: { id: string; }
},
});
});
 
protectedProcedure.query((opts) => {
const { ctx } = opts;
return ctx.user;
const ctx: { user: { id: string; }; }
});
ts
type Context = {
// user is nullable
user?: {
id: string;
};
};
 
const protectedProcedure = publicProcedure.use(async function isAuthed(opts) {
const { ctx } = opts;
// `ctx.user` is nullable
if (!ctx.user) {
(property) user: { id: string; } | undefined
throw new TRPCError({ code: 'UNAUTHORIZED' });
}
 
return opts.next({
ctx: {
// ✅ user value is known to be non-null now
user: ctx.user,
(property) user: { id: string; }
},
});
});
 
protectedProcedure.query((opts) => {
const { ctx } = opts;
return ctx.user;
const ctx: { user: { id: string; }; }
});

Använda .concat() för att skapa återanvändbara middlewares och plugins

tips
  • Att skapa middlewares med t.middleware har begränsningen att Context-typen är bunden till tRPC-instansens Context-typ.
  • Att skapa middlewares med experimental_standaloneMiddleware() har begränsningen att du inte kan definiera input-parsers och liknande knutet till din modul.

tRPC har ett API som heter .concat() som låter dig oberoende definiera en partiell procedur som kan användas med vilken tRPC-instans som helst som matchar pluginets kontext och metadata.

Detta verktyg riktar sig främst till att skapa plugins och bibliotek med tRPC.

ts
// ------------------------------------------------
// 🧩🧩🧩 a library creating a reusable plugin 🧩🧩🧩
// @filename: myPlugin.ts
 
import { initTRPC, TRPCError } from '@trpc/server';
 
export function createMyPlugin() {
// When creating a plugin for tRPC, you use the same API as creating any other tRPC-app
// this is the plugin's root `t`-object
const t = initTRPC
.context<{
// the procedure using the plugin will need to extend this context
}>()
.meta<{
// the base `initTRPC`-object of the application using this needs to extend this meta
}>()
.create();
 
return {
// you can also add `.input()` if you want your plugin to do input validation
pluginProc: t.procedure.use((opts) => {
return opts.next({
ctx: {
fromPlugin: 'hello from myPlugin' as const,
},
});
}),
};
}
// ------------------------------------
// 🚀🚀🚀 the app using the plugin 🚀🚀🚀
// @filename: app.ts
import { createMyPlugin } from './myPlugin';
import { initTRPC, TRPCError } from '@trpc/server';
 
 
// the app's root `t`-object
const t = initTRPC
.context<{
// ...
}>()
.create();
 
 
export const publicProcedure = t.procedure;
export const router = t.router;
 
// initialize the plugin (a real-world example would likely take options here)
const plugin = createMyPlugin();
 
// create a base procedure using the plugin
const procedureWithPlugin = publicProcedure
.concat(
plugin.pluginProc,
)
.use(opts => {
const { ctx } = opts;
const ctx: { fromPlugin: "hello from myPlugin"; }
return opts.next()
})
 
 
export const appRouter = router({
hello: procedureWithPlugin.query(opts => {
return opts.ctx.fromPlugin;
})
})
ts
// ------------------------------------------------
// 🧩🧩🧩 a library creating a reusable plugin 🧩🧩🧩
// @filename: myPlugin.ts
 
import { initTRPC, TRPCError } from '@trpc/server';
 
export function createMyPlugin() {
// When creating a plugin for tRPC, you use the same API as creating any other tRPC-app
// this is the plugin's root `t`-object
const t = initTRPC
.context<{
// the procedure using the plugin will need to extend this context
}>()
.meta<{
// the base `initTRPC`-object of the application using this needs to extend this meta
}>()
.create();
 
return {
// you can also add `.input()` if you want your plugin to do input validation
pluginProc: t.procedure.use((opts) => {
return opts.next({
ctx: {
fromPlugin: 'hello from myPlugin' as const,
},
});
}),
};
}
// ------------------------------------
// 🚀🚀🚀 the app using the plugin 🚀🚀🚀
// @filename: app.ts
import { createMyPlugin } from './myPlugin';
import { initTRPC, TRPCError } from '@trpc/server';
 
 
// the app's root `t`-object
const t = initTRPC
.context<{
// ...
}>()
.create();
 
 
export const publicProcedure = t.procedure;
export const router = t.router;
 
// initialize the plugin (a real-world example would likely take options here)
const plugin = createMyPlugin();
 
// create a base procedure using the plugin
const procedureWithPlugin = publicProcedure
.concat(
plugin.pluginProc,
)
.use(opts => {
const { ctx } = opts;
const ctx: { fromPlugin: "hello from myPlugin"; }
return opts.next()
})
 
 
export const appRouter = router({
hello: procedureWithPlugin.query(opts => {
return opts.ctx.fromPlugin;
})
})

Utöka middlewares

info

Vi har prefixat detta med unstable_ eftersom det är ett nytt API, men det är säkert att använda! Läs mer.

Vi har en kraftfull funktion som heter .pipe() som låter dig utöka middlewares på ett typsäkert sätt.

Nedan visas ett exempel på en middleware som utökar en bas-middleware(foo). Liksom exemplet med kontextutökning ovan kommer pipning av middlewares att ändra kontextegenskaper, och procedurer kommer att få det nya kontextvärdet.

ts
const fooMiddleware = t.middleware((opts) => {
return opts.next({
ctx: {
foo: 'foo' as const,
},
});
});
 
const barMiddleware = fooMiddleware.unstable_pipe((opts) => {
const { ctx } = opts;
ctx.foo;
(property) foo: "foo"
return opts.next({
ctx: {
bar: 'bar' as const,
},
});
});
 
const barProcedure = publicProcedure.use(barMiddleware);
barProcedure.query((opts) => {
const { ctx } = opts;
return ctx.bar;
const ctx: { foo: "foo"; bar: "bar"; }
});
ts
const fooMiddleware = t.middleware((opts) => {
return opts.next({
ctx: {
foo: 'foo' as const,
},
});
});
 
const barMiddleware = fooMiddleware.unstable_pipe((opts) => {
const { ctx } = opts;
ctx.foo;
(property) foo: "foo"
return opts.next({
ctx: {
bar: 'bar' as const,
},
});
});
 
const barProcedure = publicProcedure.use(barMiddleware);
barProcedure.query((opts) => {
const { ctx } = opts;
return ctx.bar;
const ctx: { foo: "foo"; bar: "bar"; }
});

Observera att ordningen du piper dina middlewares i spelar roll och att kontexten måste överlappa. Ett exempel på en förbjuden pipe visas nedan. Här åsidosätter fooMiddleware ctx.a medan barMiddleware fortfarande förväntar sig rotkontexten från initieringen i initTRPC - så pipning av fooMiddleware med barMiddleware fungerar inte, medan pipning av barMiddleware med fooMiddleware fungerar.

ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC
.context<{
a: {
b: 'a';
};
}>()
.create();
 
const fooMiddleware = t.middleware((opts) => {
const { ctx } = opts;
ctx.a; // 👈 fooMiddleware expects `ctx.a` to be an object
(property) a: { b: "a"; }
return opts.next({
ctx: {
a: 'a' as const, // 👈 `ctx.a` is no longer an object
},
});
});
 
const barMiddleware = t.middleware((opts) => {
const { ctx } = opts;
ctx.a; // 👈 barMiddleware expects `ctx.a` to be an object
(property) a: { b: "a"; }
return opts.next({
ctx: {
foo: 'foo' as const,
},
});
});
 
// ❌ `ctx.a` does not overlap from `fooMiddleware` to `barMiddleware`
fooMiddleware.unstable_pipe(barMiddleware);
Argument of type 'MiddlewareBuilder<{ a: { b: "a"; }; }, object, { foo: "foo"; }, unknown>' is not assignable to parameter of type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { a: "a"; }, { foo: "foo"; }, unknown> | MiddlewareBuilder<{ a: "a"; }, object, { foo: "foo"; }, unknown>'. Type 'MiddlewareBuilder<{ a: { b: "a"; }; }, object, { foo: "foo"; }, unknown>' is not assignable to type 'MiddlewareBuilder<{ a: "a"; }, object, { foo: "foo"; }, unknown>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, $ContextOverridesOut, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, $ContextOverridesOut, unknown>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, $ContextOverridesOut, unknown> | MiddlewareBuilder<{ a: "a"; foo: "foo"; }, object, $ContextOverridesOut, unknown>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: "a"; foo: "foo"; }, object, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, any, unknown>'. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, any, unknown>'. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { a: { b: "a"; }; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { a: "a"; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'ctx.a' are incompatible between these types. Type '{ b: "a"; }' is not assignable to type '"a"'.2345Argument of type 'MiddlewareBuilder<{ a: { b: "a"; }; }, object, { foo: "foo"; }, unknown>' is not assignable to parameter of type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { a: "a"; }, { foo: "foo"; }, unknown> | MiddlewareBuilder<{ a: "a"; }, object, { foo: "foo"; }, unknown>'. Type 'MiddlewareBuilder<{ a: { b: "a"; }; }, object, { foo: "foo"; }, unknown>' is not assignable to type 'MiddlewareBuilder<{ a: "a"; }, object, { foo: "foo"; }, unknown>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, $ContextOverridesOut, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, $ContextOverridesOut, unknown>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, $ContextOverridesOut, unknown> | MiddlewareBuilder<{ a: "a"; foo: "foo"; }, object, $ContextOverridesOut, unknown>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: "a"; foo: "foo"; }, object, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, any, unknown>'. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, any, unknown>'. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { a: { b: "a"; }; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { a: "a"; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'ctx.a' are incompatible between these types. Type '{ b: "a"; }' is not assignable to type '"a"'.
 
// ✅ `ctx.a` overlaps from `barMiddleware` and `fooMiddleware`
barMiddleware.unstable_pipe(fooMiddleware);
ts
import { initTRPC } from '@trpc/server';
 
const t = initTRPC
.context<{
a: {
b: 'a';
};
}>()
.create();
 
const fooMiddleware = t.middleware((opts) => {
const { ctx } = opts;
ctx.a; // 👈 fooMiddleware expects `ctx.a` to be an object
(property) a: { b: "a"; }
return opts.next({
ctx: {
a: 'a' as const, // 👈 `ctx.a` is no longer an object
},
});
});
 
const barMiddleware = t.middleware((opts) => {
const { ctx } = opts;
ctx.a; // 👈 barMiddleware expects `ctx.a` to be an object
(property) a: { b: "a"; }
return opts.next({
ctx: {
foo: 'foo' as const,
},
});
});
 
// ❌ `ctx.a` does not overlap from `fooMiddleware` to `barMiddleware`
fooMiddleware.unstable_pipe(barMiddleware);
Argument of type 'MiddlewareBuilder<{ a: { b: "a"; }; }, object, { foo: "foo"; }, unknown>' is not assignable to parameter of type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { a: "a"; }, { foo: "foo"; }, unknown> | MiddlewareBuilder<{ a: "a"; }, object, { foo: "foo"; }, unknown>'. Type 'MiddlewareBuilder<{ a: { b: "a"; }; }, object, { foo: "foo"; }, unknown>' is not assignable to type 'MiddlewareBuilder<{ a: "a"; }, object, { foo: "foo"; }, unknown>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, $ContextOverridesOut, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, $ContextOverridesOut, unknown>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, $ContextOverridesOut, unknown> | MiddlewareBuilder<{ a: "a"; foo: "foo"; }, object, $ContextOverridesOut, unknown>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: "a"; foo: "foo"; }, object, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, any, unknown>'. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, any, unknown>'. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { a: { b: "a"; }; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { a: "a"; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'ctx.a' are incompatible between these types. Type '{ b: "a"; }' is not assignable to type '"a"'.2345Argument of type 'MiddlewareBuilder<{ a: { b: "a"; }; }, object, { foo: "foo"; }, unknown>' is not assignable to parameter of type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { a: "a"; }, { foo: "foo"; }, unknown> | MiddlewareBuilder<{ a: "a"; }, object, { foo: "foo"; }, unknown>'. Type 'MiddlewareBuilder<{ a: { b: "a"; }; }, object, { foo: "foo"; }, unknown>' is not assignable to type 'MiddlewareBuilder<{ a: "a"; }, object, { foo: "foo"; }, unknown>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, $ContextOverridesOut, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, $ContextOverridesOut, unknown>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, $ContextOverridesOut, unknown> | MiddlewareBuilder<{ a: "a"; foo: "foo"; }, object, $ContextOverridesOut, unknown>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: "a"; foo: "foo"; }, object, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, any, unknown>'. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown> | MiddlewareBuilder<{ a: { b: "a"; }; foo: "foo"; }, object, any, unknown>'. Type 'MiddlewareFunction<{ a: "a"; }, object, { foo: "foo"; }, any, unknown>' is not assignable to type 'MiddlewareFunction<{ a: { b: "a"; }; }, object, { foo: "foo"; }, any, unknown>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { a: { b: "a"; }; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { a: "a"; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'ctx.a' are incompatible between these types. Type '{ b: "a"; }' is not assignable to type '"a"'.
 
// ✅ `ctx.a` overlaps from `barMiddleware` and `fooMiddleware`
barMiddleware.unstable_pipe(fooMiddleware);

Experimentell: fristående middlewares

info

Detta är inaktuellt till förmån för .concat()

tRPC har ett experimentellt API som heter experimental_standaloneMiddleware som låter dig oberoende definiera en middleware som kan användas med vilken tRPC-instans som helst. Att skapa middlewares med t.middleware har begränsningen att Context-typen är bunden till tRPC-instansens Context-typ. Det innebär att du inte kan använda samma middleware med flera tRPC-instanser som har olika Context-typer.

Med experimental_standaloneMiddleware kan du skapa en middleware som explicit definierar sina krav, dvs. Context-, Input- och Meta-typer:

ts
import {
experimental_standaloneMiddleware,
initTRPC,
TRPCError,
} from '@trpc/server';
import * as z from 'zod';
 
const projectAccessMiddleware = experimental_standaloneMiddleware<{
ctx: { allowedProjects: string[] }; // defaults to 'object' if not defined
input: { projectId: string }; // defaults to 'unknown' if not defined
// 'meta', not defined here, defaults to 'object | undefined'
}>().create((opts) => {
if (!opts.ctx.allowedProjects.includes(opts.input.projectId)) {
throw new TRPCError({
code: 'FORBIDDEN',
message: 'Not allowed',
});
}
 
return opts.next();
});
 
const t1 = initTRPC
.context<{
allowedProjects: string[];
}>()
.create();
 
// ✅ `ctx.allowedProjects` satisfies "string[]" and `input.projectId` satisfies "string"
const accessControlledProcedure = t1.procedure
.input(z.object({ projectId: z.string() }))
.use(projectAccessMiddleware);
 
// ❌ `ctx.allowedProjects` satisfies "string[]" but `input.projectId` does not satisfy "string"
const accessControlledProcedure2 = t1.procedure
.input(z.object({ projectId: z.number() }))
.use(projectAccessMiddleware);
Argument of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to parameter of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: number; }> | MiddlewareFunction<{ allowedProjects: string[]; }, object, object, object, { projectId: number; }>'. Type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: number; }>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: number; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: number; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'input.projectId' are incompatible between these types. Type 'string' is not assignable to type 'number'.2345Argument of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to parameter of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: number; }> | MiddlewareFunction<{ allowedProjects: string[]; }, object, object, object, { projectId: number; }>'. Type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: number; }>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: number; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: number; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'input.projectId' are incompatible between these types. Type 'string' is not assignable to type 'number'.
 
// ❌ `ctx.allowedProjects` does not satisfy "string[]" even though `input.projectId` satisfies "string"
const t2 = initTRPC
.context<{
allowedProjects: number[];
}>()
.create();
 
const accessControlledProcedure3 = t2.procedure
.input(z.object({ projectId: z.string() }))
.use(projectAccessMiddleware);
Argument of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to parameter of type 'MiddlewareBuilder<{ allowedProjects: number[]; }, object, object, { projectId: string; }> | MiddlewareFunction<{ allowedProjects: number[]; }, object, object, object, { projectId: string; }>'. Type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to type 'MiddlewareBuilder<{ allowedProjects: number[]; }, object, object, { projectId: string; }>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: number[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: number[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: number[]; }, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: number[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'ctx.allowedProjects' are incompatible between these types. Type 'string[]' is not assignable to type 'number[]'. Type 'string' is not assignable to type 'number'.2345Argument of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to parameter of type 'MiddlewareBuilder<{ allowedProjects: number[]; }, object, object, { projectId: string; }> | MiddlewareFunction<{ allowedProjects: number[]; }, object, object, object, { projectId: string; }>'. Type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to type 'MiddlewareBuilder<{ allowedProjects: number[]; }, object, object, { projectId: string; }>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: number[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: number[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: number[]; }, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: number[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'ctx.allowedProjects' are incompatible between these types. Type 'string[]' is not assignable to type 'number[]'. Type 'string' is not assignable to type 'number'.
ts
import {
experimental_standaloneMiddleware,
initTRPC,
TRPCError,
} from '@trpc/server';
import * as z from 'zod';
 
const projectAccessMiddleware = experimental_standaloneMiddleware<{
ctx: { allowedProjects: string[] }; // defaults to 'object' if not defined
input: { projectId: string }; // defaults to 'unknown' if not defined
// 'meta', not defined here, defaults to 'object | undefined'
}>().create((opts) => {
if (!opts.ctx.allowedProjects.includes(opts.input.projectId)) {
throw new TRPCError({
code: 'FORBIDDEN',
message: 'Not allowed',
});
}
 
return opts.next();
});
 
const t1 = initTRPC
.context<{
allowedProjects: string[];
}>()
.create();
 
// ✅ `ctx.allowedProjects` satisfies "string[]" and `input.projectId` satisfies "string"
const accessControlledProcedure = t1.procedure
.input(z.object({ projectId: z.string() }))
.use(projectAccessMiddleware);
 
// ❌ `ctx.allowedProjects` satisfies "string[]" but `input.projectId` does not satisfy "string"
const accessControlledProcedure2 = t1.procedure
.input(z.object({ projectId: z.number() }))
.use(projectAccessMiddleware);
Argument of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to parameter of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: number; }> | MiddlewareFunction<{ allowedProjects: string[]; }, object, object, object, { projectId: number; }>'. Type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: number; }>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: number; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: number; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'input.projectId' are incompatible between these types. Type 'string' is not assignable to type 'number'.2345Argument of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to parameter of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: number; }> | MiddlewareFunction<{ allowedProjects: string[]; }, object, object, object, { projectId: number; }>'. Type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: number; }>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: number; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: number; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: number; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'input.projectId' are incompatible between these types. Type 'string' is not assignable to type 'number'.
 
// ❌ `ctx.allowedProjects` does not satisfy "string[]" even though `input.projectId` satisfies "string"
const t2 = initTRPC
.context<{
allowedProjects: number[];
}>()
.create();
 
const accessControlledProcedure3 = t2.procedure
.input(z.object({ projectId: z.string() }))
.use(projectAccessMiddleware);
Argument of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to parameter of type 'MiddlewareBuilder<{ allowedProjects: number[]; }, object, object, { projectId: string; }> | MiddlewareFunction<{ allowedProjects: number[]; }, object, object, object, { projectId: string; }>'. Type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to type 'MiddlewareBuilder<{ allowedProjects: number[]; }, object, object, { projectId: string; }>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: number[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: number[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: number[]; }, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: number[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'ctx.allowedProjects' are incompatible between these types. Type 'string[]' is not assignable to type 'number[]'. Type 'string' is not assignable to type 'number'.2345Argument of type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to parameter of type 'MiddlewareBuilder<{ allowedProjects: number[]; }, object, object, { projectId: string; }> | MiddlewareFunction<{ allowedProjects: number[]; }, object, object, object, { projectId: string; }>'. Type 'MiddlewareBuilder<{ allowedProjects: string[]; }, object, object, { projectId: string; }>' is not assignable to type 'MiddlewareBuilder<{ allowedProjects: number[]; }, object, object, { projectId: string; }>'. Types of property 'unstable_pipe' are incompatible. Type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: string[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>' is not assignable to type '<$ContextOverridesOut>(fn: MiddlewareFunction<{ allowedProjects: number[]; }, object, object, $ContextOverridesOut, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: number[]; }, object, $ContextOverridesOut, { ...; }>) => MiddlewareBuilder<...>'. Types of parameters 'fn' and 'fn' are incompatible. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: number[]; }, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }> | MiddlewareBuilder<{ allowedProjects: string[]; }, object, any, { projectId: string; }>'. Type 'MiddlewareFunction<{ allowedProjects: number[]; }, object, object, any, { projectId: string; }>' is not assignable to type 'MiddlewareFunction<{ allowedProjects: string[]; }, object, object, any, { projectId: string; }>'. Types of parameters 'opts' and 'opts' are incompatible. Type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: number[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; signal: AbortSignal | undefined; next: { ...; }; }'. The types of 'ctx.allowedProjects' are incompatible between these types. Type 'string[]' is not assignable to type 'number[]'. Type 'string' is not assignable to type 'number'.

Här är ett exempel med flera fristående middlewares:

ts
import { experimental_standaloneMiddleware, initTRPC } from '@trpc/server';
import * as z from 'zod';
 
const t = initTRPC.create();
const schemaA = z.object({ valueA: z.string() });
const schemaB = z.object({ valueB: z.string() });
 
const valueAUppercaserMiddleware = experimental_standaloneMiddleware<{
input: z.infer<typeof schemaA>;
}>().create((opts) => {
return opts.next({
ctx: { valueAUppercase: opts.input.valueA.toUpperCase() },
});
});
 
const valueBUppercaserMiddleware = experimental_standaloneMiddleware<{
input: z.infer<typeof schemaB>;
}>().create((opts) => {
return opts.next({
ctx: { valueBUppercase: opts.input.valueB.toUpperCase() },
});
});
 
const combinedInputThatSatisfiesBothMiddlewares = z.object({
valueA: z.string(),
valueB: z.string(),
extraProp: z.string(),
});
 
t.procedure
.input(combinedInputThatSatisfiesBothMiddlewares)
.use(valueAUppercaserMiddleware)
.use(valueBUppercaserMiddleware)
.query(
({
input: { valueA, valueB, extraProp },
ctx: { valueAUppercase, valueBUppercase },
}) =>
`valueA: ${valueA}, valueB: ${valueB}, extraProp: ${extraProp}, valueAUppercase: ${valueAUppercase}, valueBUppercase: ${valueBUppercase}`,
);
ts
import { experimental_standaloneMiddleware, initTRPC } from '@trpc/server';
import * as z from 'zod';
 
const t = initTRPC.create();
const schemaA = z.object({ valueA: z.string() });
const schemaB = z.object({ valueB: z.string() });
 
const valueAUppercaserMiddleware = experimental_standaloneMiddleware<{
input: z.infer<typeof schemaA>;
}>().create((opts) => {
return opts.next({
ctx: { valueAUppercase: opts.input.valueA.toUpperCase() },
});
});
 
const valueBUppercaserMiddleware = experimental_standaloneMiddleware<{
input: z.infer<typeof schemaB>;
}>().create((opts) => {
return opts.next({
ctx: { valueBUppercase: opts.input.valueB.toUpperCase() },
});
});
 
const combinedInputThatSatisfiesBothMiddlewares = z.object({
valueA: z.string(),
valueB: z.string(),
extraProp: z.string(),
});
 
t.procedure
.input(combinedInputThatSatisfiesBothMiddlewares)
.use(valueAUppercaserMiddleware)
.use(valueBUppercaserMiddleware)
.query(
({
input: { valueA, valueB, extraProp },
ctx: { valueAUppercase, valueBUppercase },
}) =>
`valueA: ${valueA}, valueB: ${valueB}, extraProp: ${extraProp}, valueAUppercase: ${valueAUppercase}, valueBUppercase: ${valueBUppercase}`,
);