Fetch / 엣지 런타임 어댑터
이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →
WinterCG 표준, 특히 최소 공통 웹 플랫폼 API 사양을 따르는 모든 엣지 런타임에서 tRPC 서버를 생성할 수 있습니다.
이러한 런타임에는 다음이 포함되지만 이에 국한되지 않습니다:
-
Cloudflare Workers
-
Deno Deploy
-
Vercel Edge Runtime (& Next.js Edge Runtime)
이는 웹 플랫폼 API를 사용해 요청과 응답을 표현하는 프레임워크와의 통합도 용이하게 합니다:
-
Astro (SSR 모드)
-
Remix
-
SolidStart
예제 애플리케이션
| Description | Links |
|---|---|
| Cloudflare Workers example | Source |
| Deno Deploy example | Source |
| Next.js Edge Runtime example | Source |
| Vercel Edge Runtime example | Source |
엣지 런타임에서 tRPC 서버 사용 방법
tRPC는 네이티브 Request 및 Response API를 입출력으로 사용하는 fetch 어댑터를 제공합니다. tRPC 관련 코드는 모든 런타임에서 동일하며 응답 반환 방식만 다릅니다.
tRPC는 기본적으로 네이티브 Fetch API용 어댑터를 포함합니다. 이 어댑터를 사용하면 tRPC 라우터를 Response 객체를 반환하는 Request 핸들러로 변환할 수 있습니다.
필수 웹 API
tRPC 서버는 다음 Fetch API를 사용합니다:
-
Request,Response -
fetch -
Headers -
URL
런타임이 이러한 API를 지원한다면 tRPC 서버를 사용할 수 있습니다.
재미있는 사실: 이는 브라우저 내에서도 tRPC 서버를 사용할 수 있음을 의미합니다!
공통 설정
의존성 설치
Deno Deploy를 사용하는 경우 이 단계를 건너뛸 수 있습니다.
- npm
- yarn
- pnpm
- bun
- deno
npm install @trpc/server @trpc/client zod
yarn add @trpc/server @trpc/client zod
pnpm add @trpc/server @trpc/client zod
bun add @trpc/server @trpc/client zod
deno add npm:@trpc/server npm:@trpc/client npm:zod
Zod는 필수 의존성이 아니지만, 아래 샘플 라우터에서 사용됩니다.
라우터 생성
먼저 쿼리, 뮤테이션, 서브스크립션을 처리할 라우터가 필요합니다.
아래에 샘플 라우터가 있습니다. router.ts 파일로 저장하세요.
router.ts
router.tstsimport { 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 APIexport type AppRouter = typeof appRouter;
router.tstsimport { 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 APIexport type AppRouter = typeof appRouter;
라우터 파일이 너무 커지기 시작하면, 라우터를 여러 서브 라우터로 분할하고 각각을 별도의 파일로 구현하세요. 그런 다음 이들을 병합하여 단일 루트 appRouter로 만드세요.
컨텍스트 생성
그런 다음 각 요청마다 생성될 컨텍스트가 필요합니다.
아래에 샘플 컨텍스트가 있습니다. context.ts 파일로 저장하세요:
context.ts
context.tstsimport { 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.tstsimport { 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].tstsimport { 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].tstsimport { 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.tstsimport { 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.tstsimport { 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를 통해 사용 가능해집니다!
| Endpoint | HTTP URI |
|---|---|
getUser | GET http://localhost:8787/trpc/getUserById?input=INPUT where INPUT is a URI-encoded JSON string. |
createUser | POST http://localhost:8787/trpc/createUser with req.body of type User |
Deno Oak
이 가이드는 Deno가 설치 및 설정되어 있다고 가정합니다. 자세한 내용은 Deno의 시작하기 가이드를 참조하세요.
router.ts에서 import 업데이트하기
router.tstsimport { initTRPC } from 'npm:@trpc/server';import { z } from 'npm:zod';import { Context } from './context.ts';
router.tstsimport { initTRPC } from 'npm:@trpc/server';import { z } from 'npm:zod';import { Context } from './context.ts';
context.ts에서 import 업데이트하기
context.tstsimport { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
context.tstsimport { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
Oak에서 fetchRequestHandler 사용하기 (app.ts)
app.tstsimport { 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.tstsimport { 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에서 import 업데이트하기
router.tstsimport { initTRPC } from 'npm:@trpc/server';import { z } from 'npm:zod';import { Context } from './context.ts';
router.tstsimport { initTRPC } from 'npm:@trpc/server';import { z } from 'npm:zod';import { Context } from './context.ts';
context.ts에서 import 업데이트하기
context.tstsimport { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
context.tstsimport { FetchCreateContextFnOptions } from 'npm:@trpc/server/adapters/fetch';
Deno Deploy 함수 생성하기
server.tstsimport { 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.tstsimport { 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 명령어를 실행하면 엔드포인트가 HTTP를 통해 사용 가능해집니다!
| Endpoint | HTTP URI |
|---|---|
getUser | GET http://localhost:8000/trpc/getUserById?input=INPUT where INPUT is a URI-encoded JSON string. |
createUser | POST http://localhost:8000/trpc/createUser with req.body of type User |
Next.js 에지 런타임
전체 예제는 여기를 참조하세요.
Remix
app/routes/trpc.$trpc.tstsimport 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.tstsimport 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].tstsimport { 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].tstsimport { 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 에지 런타임
자세한 내용은 공식 Vercel 에지 런타임 문서를 참조하세요.
작동 예제는 저희 Vercel 에지 런타임 앱 샘플을 참조하세요.
의존성 설치
- npm
- yarn
- pnpm
- bun
shnpm install -g edge-runtime
shnpm install -g edge-runtime
shyarn global add edge-runtime
shyarn global add edge-runtime
shpnpm add -g edge-runtime
shpnpm add -g edge-runtime
shbun add -g edge-runtime
shbun add -g edge-runtime
에지 런타임 함수 생성
server.tstsimport { 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.tstsimport { 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 명령어를 실행하면 엔드포인트가 HTTP를 통해 사용 가능해집니다!
| Endpoint | HTTP URI |
|---|---|
getUser | GET http://localhost:3000/trpc/getUserById?input=INPUT where INPUT is a URI-encoded JSON string. |
createUser | POST http://localhost:3000/trpc/createUser with req.body of type User |