본문 바로가기
버전: 10.x

Fetch / 엣지 런타임 어댑터

비공식 베타 번역

이 페이지는 PageTurner AI로 번역되었습니다(베타). 프로젝트 공식 승인을 받지 않았습니다. 오류를 발견하셨나요? 문제 신고 →

WinterCG 표준, 특히 최소 공통 웹 플랫폼 API 사양을 따르는 모든 엣지 런타임에서 tRPC 서버를 생성할 수 있습니다.

이러한 런타임에는 다음이 포함되지만 이에 국한되지 않습니다:

  • Cloudflare Workers

  • Deno Deploy

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

이는 웹 플랫폼 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는 네이티브 RequestResponse API를 입출력으로 사용하는 fetch 어댑터를 제공합니다. tRPC 관련 코드는 모든 런타임에서 동일하며 응답 반환 방식만 다릅니다.

tRPC는 기본적으로 네이티브 Fetch API용 어댑터를 포함합니다. 이 어댑터를 사용하면 tRPC 라우터를 Response 객체를 반환하는 Request 핸들러로 변환할 수 있습니다.

필수 웹 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에서 import 업데이트하기

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에서 import 업데이트하기

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에서 import 업데이트하기

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에서 import 업데이트하기

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 명령어를 실행하면 엔드포인트가 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 에지 런타임

전체 예제는 여기를 참조하세요.

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 에지 런타임

참고

자세한 내용은 공식 Vercel 에지 런타임 문서를 참조하세요.

작동 예제는 저희 Vercel 에지 런타임 앱 샘플을 참조하세요.

의존성 설치

sh
npm install -g edge-runtime
sh
npm install -g 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 명령어를 실행하면 엔드포인트가 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