跳至主内容
版本:11.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 适配器,它使用原生 Request 和 Response API 作为输入输出。所有运行时的 tRPC 核心代码相同,仅响应返回方式存在差异。

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

必需的 Web API​

tRPC 服务器使用以下 Fetch API:

  • Request, Response

  • fetch

  • Headers

  • URL

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

技巧

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

通用设置​

安装依赖​

技巧

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

npm install @trpc/server @trpc/client zod

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

AI 代理

若您使用 AI 编程代理,请安装 tRPC 技能以优化代码生成效果:

bash
npx @tanstack/intent@latest install
bash
npx @tanstack/intent@latest install

创建路由器​

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

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

router.ts
router.ts
ts
import { initTRPC } from '@trpc/server';
import { z } from 'zod';
import type { 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 type { 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 type { 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 type { 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 { ActionFunctionArgs, LoaderFunctionArgs } 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: LoaderFunctionArgs) => {
return handleRequest(args);
};
export const action = async (args: ActionFunctionArgs) => {
return handleRequest(args);
};
function handleRequest(args: LoaderFunctionArgs | ActionFunctionArgs) {
return fetchRequestHandler({
endpoint: '/trpc',
req: args.request,
router: appRouter,
createContext,
});
}
app/routes/trpc.$trpc.ts
ts
import type { ActionFunctionArgs, LoaderFunctionArgs } 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: LoaderFunctionArgs) => {
return handleRequest(args);
};
export const action = async (args: ActionFunctionArgs) => {
return handleRequest(args);
};
function handleRequest(args: LoaderFunctionArgs | ActionFunctionArgs) {
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 '@solidjs/start/server';
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 '@solidjs/start/server';
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';
 
// Vercel Edge Runtime uses Service Worker-style addEventListener
addEventListener('fetch', (event: any) => {
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';
 
// Vercel Edge Runtime uses Service Worker-style addEventListener
addEventListener('fetch', (event: any) => {
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