本文へスキップ
バージョン: 11.x

ミドルウェア

t.procedure.use()メソッドを使用して、プロシージャにミドルウェアを追加できます。ミドルウェアはプロシージャの呼び出しをラップし、その戻り値を渡す必要があります。

認証

以下の例では、adminProcedureへの呼び出しは、実行前にユーザーが「admin」であることを確認します。

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

上記の例で発生するTRPCErrorの詳細については、エラー処理を参照してください。

ロギング

以下の例では、クエリの実行時間は自動的にログに記録されます。

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

コンテキスト拡張

「コンテキスト拡張」により、ミドルウェアは型安全な方法で基本プロシージャのコンテキストのキーを動的に追加および上書きできます。

以下は、コンテキストのプロパティを変更するミドルウェアの例です。変更されたプロパティは、他のミドルウェアやプロシージャなどの、後続のすべてのコンシューマーで使用できます。

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(({ ctx }) => ctx.user);
(property) 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(({ ctx }) => ctx.user);
(property) user: { id: string; }

再利用可能なミドルウェアとプラグインを作成するための.concat()の使用

情報

これは新しいAPIであるため、unstable_というプレフィックスを付けていますが、安心して使用できます!詳細はこちら

ヒント
  • t.middlewareを使用してミドルウェアを作成すると、Context型がtRPCインスタンスのContext型に結び付けられるという制限があります。
  • experimental_standaloneMiddleware()を使用してミドルウェアを作成すると、モジュールに関連付けられた入力パーサーなどを定義できないという制限があります。

tRPCには、コンテキストとメタデータが一致する任意のtRPCインスタンスで使用できる部分的なプロシージャを独立して定義できる.concat()と呼ばれるAPIがあります。

このヘルパーは主に、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
.unstable_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
.unstable_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;
})
})

ミドルウェアの拡張

情報

これは新しいAPIであるため、unstable_というプレフィックスを付けていますが、安心して使用できます!詳細はこちら

型安全な方法でミドルウェアを拡張できる強力な機能.pipe()があります。

以下は、基本ミドルウェア(foo)を拡張するミドルウェアの例です。上記のコンテキスト拡張の例と同様に、ミドルウェアのパイプライン処理によってコンテキストのプロパティが変更され、プロシージャは新しいコンテキスト値を受け取ります。

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(({ ctx }) => ctx.bar);
(parameter) 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(({ ctx }) => ctx.bar);
(parameter) ctx: { foo: "foo"; bar: "bar"; }

ミドルウェアをパイプする順序が重要であり、コンテキストが重複する必要があることに注意してください。禁止されているパイプの例を以下に示します。ここでは、fooMiddlewarectx.aを上書きしますが、barMiddlewareinitTRPCでの初期化からのルートコンテキストを依然として期待しているため、fooMiddlewarebarMiddlewareをパイプすることはできませんが、barMiddlewarefooMiddlewareをパイプすることはできます。

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; next: { (): Promise<MiddlewareResult<{ foo: "foo"; }>>; <$ContextOverride>(opts: { ...; }): Promise<...>; (opts: { ...; }): Promise<...>; }; }' is not assignable to type '{ ctx: { a: "a"; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; next: { (): Promise<MiddlewareResult<{ foo: "foo"; }>>; <$ContextOverride>(opts: { ...; }): Promise<...>; (opts: { ...; }): Promise<...>; }; }'. 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; next: { (): Promise<MiddlewareResult<{ foo: "foo"; }>>; <$ContextOverride>(opts: { ...; }): Promise<...>; (opts: { ...; }): Promise<...>; }; }' is not assignable to type '{ ctx: { a: "a"; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; next: { (): Promise<MiddlewareResult<{ foo: "foo"; }>>; <$ContextOverride>(opts: { ...; }): Promise<...>; (opts: { ...; }): Promise<...>; }; }'. 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; next: { (): Promise<MiddlewareResult<{ foo: "foo"; }>>; <$ContextOverride>(opts: { ...; }): Promise<...>; (opts: { ...; }): Promise<...>; }; }' is not assignable to type '{ ctx: { a: "a"; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; next: { (): Promise<MiddlewareResult<{ foo: "foo"; }>>; <$ContextOverride>(opts: { ...; }): Promise<...>; (opts: { ...; }): Promise<...>; }; }'. 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; next: { (): Promise<MiddlewareResult<{ foo: "foo"; }>>; <$ContextOverride>(opts: { ...; }): Promise<...>; (opts: { ...; }): Promise<...>; }; }' is not assignable to type '{ ctx: { a: "a"; foo: "foo"; }; type: "query" | "mutation" | "subscription"; path: string; input: unknown; getRawInput: GetRawInputFn; meta: object | undefined; next: { (): Promise<MiddlewareResult<{ foo: "foo"; }>>; <$ContextOverride>(opts: { ...; }): Promise<...>; (opts: { ...; }): Promise<...>; }; }'. 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);

実験的:スタンドアロンミドルウェア

情報

これは.unstable_concat()に置き換えられました。

tRPCには、任意のtRPCインスタンスで使用できるミドルウェアを独立して定義できる実験的APIであるexperimental_standaloneMiddlewareがあります。t.middlewareを使用してミドルウェアを作成すると、Context型がtRPCインスタンスのContext型に結び付けられるという制限があります。つまり、Context型が異なる複数のtRPCインスタンスで同じミドルウェアを使用することはできません。

experimental_standaloneMiddlewareを使用すると、コンテキスト、入力、メタデータの型など、その要件を明示的に定義するミドルウェアを作成できます。

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; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: number; }; getRawInput: GetRawInputFn; meta: object | undefined; next: { ...; }; }'. Types of property 'input' are incompatible. Type '{ projectId: string; }' is not assignable to type '{ projectId: 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; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: number; }; getRawInput: GetRawInputFn; meta: object | undefined; next: { ...; }; }'. Types of property 'input' are incompatible. Type '{ projectId: string; }' is not assignable to type '{ projectId: 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; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: number[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; next: { ...; }; }'. The types of 'ctx.allowedProjects' 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: 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; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: number[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; next: { ...; }; }'. The types of 'ctx.allowedProjects' are incompatible between these types. 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; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: number; }; getRawInput: GetRawInputFn; meta: object | undefined; next: { ...; }; }'. Types of property 'input' are incompatible. Type '{ projectId: string; }' is not assignable to type '{ projectId: 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; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: string[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: number; }; getRawInput: GetRawInputFn; meta: object | undefined; next: { ...; }; }'. Types of property 'input' are incompatible. Type '{ projectId: string; }' is not assignable to type '{ projectId: 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; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: number[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; next: { ...; }; }'. The types of 'ctx.allowedProjects' 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: 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; next: { ...; }; }' is not assignable to type '{ ctx: { allowedProjects: number[]; }; type: "query" | "mutation" | "subscription"; path: string; input: { projectId: string; }; getRawInput: GetRawInputFn; meta: object | undefined; next: { ...; }; }'. The types of 'ctx.allowedProjects' are incompatible between these types. Type 'string[]' is not assignable to type 'number[]'.

複数のスタンドアロンミドルウェアの例を次に示します。

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