跳至主内容
版本:10.x

Fetch / 边缘运行时适配器

非官方测试版翻译

本页面由 PageTurner AI 翻译(测试版)。未经项目官方认可。 发现错误? 报告问题 →

您可以在遵循 WinterCG 规范的任何边缘运行时中创建 tRPC 服务器,特别是符合 最小通用 Web 平台 API 规范的运行时。

这些运行时包括但不限于:

  • Cloudflare Workers

  • Deno Deploy

  • Vercel Edge Runtime (& Next.js Edge Runtime)

这也使其能轻松集成到使用 Web 平台 API 表示请求和响应的框架中,例如:

  • Astro (SSR 模式)

  • Remix

  • SolidStart

示例应用

DescriptionLinks
Cloudflare Workers example

Source

Deno Deploy example

Source

Next.js Edge Runtime example

Source

Vercel Edge Runtime example

Source

如何在边缘运行时中使用 tRPC 服务器

tRPC 提供了 fetch 适配器,它使用原生 RequestResponse API 作为输入输出。所有运行时的 tRPC 核心代码相同,仅响应返回方式存在差异。

tRPC 开箱即用地提供了原生 Fetch API 适配器。该适配器可将您的 tRPC 路由转换为返回 Response 对象的 Request 处理器。

必需的 Web API

tRPC 服务器使用以下 Fetch API:

  • Request, Response

  • fetch

  • Headers

  • URL

若您的运行时支持这些 API,即可使用 tRPC 服务器

技巧

冷知识:这也意味着您能在浏览器中运行 tRPC 服务器!

通用设置

安装依赖

技巧

若使用 Deno Deploy 可跳过此步骤。

sh
npm install @trpc/server zod
sh
npm install @trpc/server zod

Zod 并非必需依赖,但下方示例路由中会使用到。

创建路由器

首先需要创建路由来处理查询、变更和订阅操作。

下面提供了示例路由器,将其保存为 router.ts 文件:

router.ts
router.ts
ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
import { Context } from './context';
type User = {
id: string;
name: string;
bio?: string;
};
const users: Record<string, User> = {};
export const t = initTRPC.context<Context>().create();
export const appRouter = t.router({
getUserById: t.procedure.input(z.string()).query((opts) => {
return users[opts.input]; // input type is string
}),
createUser: t.procedure
// validate input with Zod
.input(
z.object({
name: z.string().min(3),
bio: z.string().max(142).optional(),
}),
)
.mutation((opts) => {
const id = Date.now().toString();
const user: User = { id, ...opts.input };
users[user.id] = user;
return user;
}),
});
// export type definition of API
export type AppRouter = typeof appRouter;
router.ts
ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
import { Context } from './context';
type User = {
id: string;
name: string;
bio?: string;
};
const users: Record<string, User> = {};
export const t = initTRPC.context<Context>().create();
export const appRouter = t.router({
getUserById: t.procedure.input(z.string()).query((opts) => {
return users[opts.input]; // input type is string
}),
createUser: t.procedure
// validate input with Zod
.input(
z.object({
name: z.string().min(3),
bio: z.string().max(142).optional(),
}),
)
.mutation((opts) => {
const id = Date.now().toString();
const user: User = { id, ...opts.input };
users[user.id] = user;
return user;
}),
});
// export type definition of API
export type AppRouter = typeof appRouter;

若路由文件过大,可将其拆分为多个独立文件实现的子路由,然后合并为单一根路由 appRouter

创建上下文

接着需要为每个请求创建上下文

下面提供了示例上下文,将其保存为 context.ts 文件:

context.ts
context.ts
ts
import { FetchCreateContextFnOptions } from '@trpc/server/adapters/fetch';
export function createContext({
req,
resHeaders,
}: FetchCreateContextFnOptions) {
const user = { name: req.headers.get('username') ?? 'anonymous' };
return { req, resHeaders, user };
}
export type Context = Awaited<ReturnType<typeof createContext>>;
context.ts
ts
import { FetchCreateContextFnOptions } from '@trpc/server/adapters/fetch';
export function createContext({
req,
resHeaders,
}: FetchCreateContextFnOptions) {
const user = { name: req.headers.get('username') ?? 'anonymous' };
return { req, resHeaders, user };
}
export type Context = Awaited<ReturnType<typeof createContext>>;

运行时特定设置

Astro

src/pages/trpc/[trpc].ts
ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import type { APIRoute } from 'astro';
import { createContext } from '../../server/context';
import { appRouter } from '../../server/router';
export const all: APIRoute = (opts) => {
return fetchRequestHandler({
endpoint: '/trpc',
req: opts.request,
router: appRouter,
createContext,
});
};
src/pages/trpc/[trpc].ts
ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import type { APIRoute } from 'astro';
import { createContext } from '../../server/context';
import { appRouter } from '../../server/router';
export const all: APIRoute = (opts) => {
return fetchRequestHandler({
endpoint: '/trpc',
req: opts.request,
router: appRouter,
createContext,
});
};

Cloudflare Worker

备注

运行 Cloudflare Workers 需安装 Wrangler CLI

创建 Cloudflare Worker

server.ts
ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { createContext } from './context';
import { appRouter } from './router';
export default {
async fetch(request: Request): Promise<Response> {
return fetchRequestHandler({
endpoint: '/trpc',
req: request,
router: appRouter,
createContext,
});
},
};
server.ts
ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { createContext } from './context';
import { appRouter } from './router';
export default {
async fetch(request: Request): Promise<Response> {
return fetchRequestHandler({
endpoint: '/trpc',
req: request,
router: appRouter,
createContext,
});
},
};

执行 wrangler dev server.ts 命令后,您的端点将通过 HTTP 提供服务!

EndpointHTTP URI
getUserGET http://localhost:8787/trpc/getUserById?input=INPUT

where INPUT is a URI-encoded JSON string.
createUserPOST http://localhost:8787/trpc/createUser

with req.body of type User

Deno Oak

备注

本指南假设您已安装并配置好 Deno 环境。更多信息请参考 Deno 的入门指南

更新 router.ts 中的导入语句

router.ts
ts
import { initTRPC } from 'npm:@trpc/server';
import { z } from 'npm:zod';
import { Context } from './context.ts';
router.ts
ts
import { initTRPC } from 'npm:@trpc/server';
import { z } from 'npm:zod';
import { Context } from './context.ts';

更新 context.ts 中的导入语句

context.ts
ts
import { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
context.ts
ts
import { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';

在 Oak 中使用 fetchRequestHandler(位于 app.ts

app.ts
ts
import { Application, Router } from 'https://deno.land/x/oak/mod.ts';
import { fetchRequestHandler } from 'npm:@trpc/server/adapters/fetch';
import { createContext } from './context.ts';
import { appRouter } from './router.ts';
const app = new Application();
const router = new Router();
router.all('/trpc/(.*)', async (ctx) => {
const res = await fetchRequestHandler({
endpoint: '/trpc',
req: new Request(ctx.request.url, {
headers: ctx.request.headers,
body:
ctx.request.method !== 'GET' && ctx.request.method !== 'HEAD'
? ctx.request.body({ type: 'stream' }).value
: void 0,
method: ctx.request.method,
}),
router: appRouter,
createContext,
});
ctx.response.status = res.status;
ctx.response.headers = res.headers;
ctx.response.body = res.body;
});
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: 3000 });
app.ts
ts
import { Application, Router } from 'https://deno.land/x/oak/mod.ts';
import { fetchRequestHandler } from 'npm:@trpc/server/adapters/fetch';
import { createContext } from './context.ts';
import { appRouter } from './router.ts';
const app = new Application();
const router = new Router();
router.all('/trpc/(.*)', async (ctx) => {
const res = await fetchRequestHandler({
endpoint: '/trpc',
req: new Request(ctx.request.url, {
headers: ctx.request.headers,
body:
ctx.request.method !== 'GET' && ctx.request.method !== 'HEAD'
? ctx.request.body({ type: 'stream' }).value
: void 0,
method: ctx.request.method,
}),
router: appRouter,
createContext,
});
ctx.response.status = res.status;
ctx.response.headers = res.headers;
ctx.response.body = res.body;
});
app.use(router.routes());
app.use(router.allowedMethods());
await app.listen({ port: 3000 });

Deno Deploy

备注

本指南假设您已安装并配置好 Deno 环境。更多信息请参考 Deno 的入门指南

技巧

完整示例请参考我们的 Deno Deploy 应用示例

更新 router.ts 中的导入语句

router.ts
ts
import { initTRPC } from 'npm:@trpc/server';
import { z } from 'npm:zod';
import { Context } from './context.ts';
router.ts
ts
import { initTRPC } from 'npm:@trpc/server';
import { z } from 'npm:zod';
import { Context } from './context.ts';

更新 context.ts 中的导入语句

context.ts
ts
import { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
context.ts
ts
import { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';

创建 Deno Deploy 函数

server.ts
ts
import { fetchRequestHandler } from 'npm:@trpc/server/adapters/fetch';
import { createContext } from './context.ts';
import { appRouter } from './router.ts';
function handler(request) {
return fetchRequestHandler({
endpoint: '/trpc',
req: request,
router: appRouter,
createContext,
});
}
Deno.serve(handler);
server.ts
ts
import { fetchRequestHandler } from 'npm:@trpc/server/adapters/fetch';
import { createContext } from './context.ts';
import { appRouter } from './router.ts';
function handler(request) {
return fetchRequestHandler({
endpoint: '/trpc',
req: request,
router: appRouter,
createContext,
});
}
Deno.serve(handler);

执行命令 deno run --allow-net=:8000 --allow-env ./server.ts,您的 API 端点将通过 HTTP 提供服务!

EndpointHTTP URI
getUserGET http://localhost:8000/trpc/getUserById?input=INPUT

where INPUT is a URI-encoded JSON string.
createUserPOST http://localhost:8000/trpc/createUser

with req.body of type User

Next.js Edge Runtime

完整示例请参考此处

Remix

app/routes/trpc/$trpc.ts
ts
import type { ActionArgs, LoaderArgs } from '@remix-run/node';
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { createContext } from '~/server/context';
import { appRouter } from '~/server/router';
export const loader = async (args: LoaderArgs) => {
return handleRequest(args);
};
export const action = async (args: ActionArgs) => {
return handleRequest(args);
};
function handleRequest(args: LoaderArgs | ActionArgs) {
return fetchRequestHandler({
endpoint: '/trpc',
req: args.request,
router: appRouter,
createContext,
});
}
app/routes/trpc/$trpc.ts
ts
import type { ActionArgs, LoaderArgs } from '@remix-run/node';
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { createContext } from '~/server/context';
import { appRouter } from '~/server/router';
export const loader = async (args: LoaderArgs) => {
return handleRequest(args);
};
export const action = async (args: ActionArgs) => {
return handleRequest(args);
};
function handleRequest(args: LoaderArgs | ActionArgs) {
return fetchRequestHandler({
endpoint: '/trpc',
req: args.request,
router: appRouter,
createContext,
});
}

SolidStart

src/routes/api/trpc/[trpc].ts
ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import type { APIEvent } from 'solid-start';
import { createContext } from '../../server/context';
import { appRouter } from '../../server/router';
const handler = (event: APIEvent) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req: event.request,
router: appRouter,
createContext,
});
export { handler as GET, handler as POST };
src/routes/api/trpc/[trpc].ts
ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import type { APIEvent } from 'solid-start';
import { createContext } from '../../server/context';
import { appRouter } from '../../server/router';
const handler = (event: APIEvent) =>
fetchRequestHandler({
endpoint: '/api/trpc',
req: event.request,
router: appRouter,
createContext,
});
export { handler as GET, handler as POST };

Vercel Edge Runtime

备注

更多信息请参阅官方 Vercel Edge Runtime 文档

技巧

完整示例请参考我们的 Vercel Edge Runtime 应用示例

安装依赖

sh
npm install -g edge-runtime
sh
npm install -g edge-runtime

创建 Edge Runtime 函数

server.ts
ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { createContext } from './context';
import { appRouter } from './router';
addEventListener('fetch', (event) => {
return event.respondWith(
fetchRequestHandler({
endpoint: '/trpc',
req: event.request,
router: appRouter,
createContext,
}),
);
});
server.ts
ts
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';
import { createContext } from './context';
import { appRouter } from './router';
addEventListener('fetch', (event) => {
return event.respondWith(
fetchRequestHandler({
endpoint: '/trpc',
req: event.request,
router: appRouter,
createContext,
}),
);
});

执行命令 edge-runtime --listen server.ts --port 3000,您的 API 端点将通过 HTTP 提供服务!

EndpointHTTP URI
getUserGET http://localhost:3000/trpc/getUserById?input=INPUT

where INPUT is a URI-encoded JSON string.
createUserPOST http://localhost:3000/trpc/createUser

with req.body of type User