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

OpenAPI (알파)

비공식 베타 번역

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

주의

이 패키지는 알파 단계입니다. API가 사전 공지 없이 변경될 수 있습니다.

@trpc/openapi 패키지는 tRPC 라우터에서 OpenAPI 3.1 스펙을 생성합니다. 이 스펙으로 다음을 수행할 수 있습니다:

  • 모든 언어에서 타입이 지정된 API 클라이언트 생성

  • Postman이나 Insomnia 같은 HTTP 도구로 tRPC 엔드포인트 호출

  • MCP 서버 같은 AI 에이전트 통합 활성화

설치

bash
pnpm add @trpc/openapi
bash
pnpm add @trpc/openapi
AI 에이전트

AI 코딩 에이전트를 사용하는 경우 더 나은 코드 생성을 위해 tRPC 스킬을 설치하세요:

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

@trpc/openapi는 현재 11.x.x-alpha 형식으로 버저닝되며 최신 tRPC v11 버전과 호환됩니다. 하지만 항상 버전 번호를 맞추는 것을 권장합니다

tRPC 설정 적용

생성기는 기존 라우터에서 바로 작동합니다 — 어노테이션이나 데코레이터가 필요하지 않습니다. 주의해야 할 몇 가지 사항:

  • 출력 타입 불필요 — 다른 OpenAPI 도구와 달리 .output() 스키마는 선택 사항입니다. 생성기가 구현에서 반환 타입을 자동으로 추론합니다.

  • 트랜스포머 — 서버에서 데이터 변환기를 사용하는 경우 OpenAPI 클라이언트도 동일한 변환기를 사용해야 합니다. 설정 및 크로스 언어 옵션은 트랜스포머를 참조하세요.

  • 구독 — 현재 생성된 스펙에서 제외됩니다. SSE 지원이 계획 중입니다.

  • 설명 — 타입, 라우터, 프로시저의 Zod .describe() 호출과 JSDoc 주석은 모두 스펙의 description 필드로 변환됩니다.

스펙 생성

CLI

bash
pnpm exec trpc-openapi ./src/server/router.ts
bash
pnpm exec trpc-openapi ./src/server/router.ts
OptionDefaultDescription
-e, --export <name>AppRouterName of the exported router type
-o, --output <file>openapi.jsonOutput file path
--title <text>tRPC APIOpenAPI info.title
--version <ver>0.0.0OpenAPI info.version
bash
pnpm exec trpc-openapi ./src/server/router.ts -o api.json --title "My API" --version 1.0.0
bash
pnpm exec trpc-openapi ./src/server/router.ts -o api.json --title "My API" --version 1.0.0

프로그래밍 방식

scripts/generate-openapi.ts
ts
import { generateOpenAPIDocument } from '@trpc/openapi';
const doc = generateOpenAPIDocument('./src/server/router.ts', {
exportName: 'AppRouter',
title: 'My API',
version: '1.0.0',
});
scripts/generate-openapi.ts
ts
import { generateOpenAPIDocument } from '@trpc/openapi';
const doc = generateOpenAPIDocument('./src/server/router.ts', {
exportName: 'AppRouter',
title: 'My API',
version: '1.0.0',
});

생성기는 라우터의 TypeScript 타입을 정적으로 분석합니다 — 코드를 실행하지 않습니다.

스펙에서 클라이언트 생성

모든 OpenAPI 클라이언트 생성기가 작동해야 하지만, 가장 많이 테스트된 통합은 Hey API입니다.

생성된 클라이언트는 tRPC 프로시저에 매칭되는 타입이 지정된 SDK 함수를 생성합니다:

  • 쿼리GET /procedure.path

  • 뮤테이션POST /procedure.path

  • 구독은 무시됨 (SSE 지원 예정)

Hey API (TypeScript)

Hey API 문서

bash
pnpm add @trpc/openapi @hey-api/openapi-ts
bash
pnpm add @trpc/openapi @hey-api/openapi-ts

기본적으로 OpenAPI로 생성된 클라이언트는 트랜스포머 설정이나 쿼리 파라미터 인코딩 방식을 알지 못합니다. @trpc/openapi/heyapi 패키지는 configureTRPCHeyApiClient 헬퍼를 제공해 이 간극을 메웁니다 — 이 헬퍼는 요청 직렬화, 응답 파싱, 에러 역직렬화를 구성하여 생성된 SDK가 tRPC 엔드포인트와 올바르게 작동하도록 합니다.

트랜스포머 없이

이 경우 Hey API의 CLI나 프로그래밍 방식 API로 클라이언트를 생성할 수 있습니다

bash
pnpm exec openapi-ts -i openapi.json -o ./generated
bash
pnpm exec openapi-ts -i openapi.json -o ./generated

런타임에 약간의 구성이 필요합니다:

src/usage.ts
ts
import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
import { client } from './generated/client.gen';
import { Sdk } from './generated/sdk.gen';
configureTRPCHeyApiClient(client, {
baseUrl: 'http://localhost:3000',
});
const sdk = new Sdk({ client });
const result = await sdk.greeting({ query: { input: { name: 'World' } } });
const user = await sdk.user.create({ body: { name: 'Bob', age: 30 } });
src/usage.ts
ts
import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
import { client } from './generated/client.gen';
import { Sdk } from './generated/sdk.gen';
configureTRPCHeyApiClient(client, {
baseUrl: 'http://localhost:3000',
});
const sdk = new Sdk({ client });
const result = await sdk.greeting({ query: { input: { name: 'World' } } });
const user = await sdk.user.create({ body: { name: 'Bob', age: 30 } });

트랜스포머 사용 시 (superjson, devalue 등)

경고

백엔드에서 superjson 같은 데이터 변환기를 사용하는 경우 클라이언트 구성에 반드시 전달해야 합니다. 이 작업을 생략하면 Date, Map, Set 및 기타 비JSON 타입이 자동으로 잘못 변환될 수 있습니다.

먼저 Hey API의 프로그래밍 방식 API로 클라이언트 코드를 생성하세요. 이렇게 하면 createTRPCHeyApiTypeResolvers를 사용해 생성된 타입이 정확한지 보장할 수 있습니다:

src/client.ts
ts
import { createClient } from '@hey-api/openapi-ts';
import { createTRPCHeyApiTypeResolvers } from '@trpc/openapi/heyapi';
const openApiJson = './path/to/openapi.json'
const outputDir = './generated'
await createClient({
input: openApiJson,
output: outputDir,
plugins: [
{
name: '@hey-api/typescript',
// Important: this ensures that your emitted types like Dates are correct
'~resolvers': createTRPCHeyApiTypeResolvers(),
},
{
name: '@hey-api/sdk',
operations: { strategy: 'single' },
},
],
});
src/client.ts
ts
import { createClient } from '@hey-api/openapi-ts';
import { createTRPCHeyApiTypeResolvers } from '@trpc/openapi/heyapi';
const openApiJson = './path/to/openapi.json'
const outputDir = './generated'
await createClient({
input: openApiJson,
output: outputDir,
plugins: [
{
name: '@hey-api/typescript',
// Important: this ensures that your emitted types like Dates are correct
'~resolvers': createTRPCHeyApiTypeResolvers(),
},
{
name: '@hey-api/sdk',
operations: { strategy: 'single' },
},
],
});

런타임에 생성된 클라이언트를 트랜스포머로 구성하세요. 그러면 네이티브 타입을 직접 전달하고 역직렬화된 결과를 받을 수 있습니다:

src/usage.ts
ts
import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
import superjson from 'superjson';
import { client } from './generated/client.gen';
configureTRPCHeyApiClient(client, {
baseUrl: 'http://localhost:3000',
// Important, this transformer must match your tRPC API's transformer:
transformer: superjson,
});
const sdk = new Sdk({ client });
const event = await sdk.getEvent({
query: { input: { id: 'evt_1', at: new Date('2025-06-15T10:00:00Z') } },
});
// event.data.result.data.at is a Date object ✅
const created = await sdk.createEvent({
body: { name: 'Conference', at: new Date('2025-09-01T09:00:00Z') },
});
src/usage.ts
ts
import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
import superjson from 'superjson';
import { client } from './generated/client.gen';
configureTRPCHeyApiClient(client, {
baseUrl: 'http://localhost:3000',
// Important, this transformer must match your tRPC API's transformer:
transformer: superjson,
});
const sdk = new Sdk({ client });
const event = await sdk.getEvent({
query: { input: { id: 'evt_1', at: new Date('2025-06-15T10:00:00Z') } },
});
// event.data.result.data.at is a Date object ✅
const created = await sdk.createEvent({
body: { name: 'Conference', at: new Date('2025-09-01T09:00:00Z') },
});

다른 생성기나 언어 사용하기

생성된 OpenAPI 스펙은 다음 조건을 충족하는 모든 OpenAPI 호환 클라이언트 생성기와 함께 작동합니다:

  • Date 같은 클래스에 대한 정확한 타입 생성

  • Search Params 및 요청/응답 본문 직렬화 커스터마이징 지원

tRPC 프로토콜과 정확하게 통합하려면 생성된 클라이언트를 다음 두 가지를 수행하도록 설정해야 합니다:

  • 트랜스포머 — tRPC API가 트랜스포머를 사용하는 경우, 클라이언트는 동일한 포맷으로 입력을 직렬화하고 출력을 역직렬화해야 함

  • 쿼리 입력값 — GET 요청은 입력을 개별 쿼리 파라미터가 아닌 ?input=<JSON> 형식으로 인코딩함

전체 참조 구현은 Hey API 구성 소스 코드를 참조하세요.

트랜스포머

tRPC 데이터 트랜스포머를 사용하면 Date, Map, Set, BigInt 같은 풍부한 타입을 네트워크로 전송할 수 있습니다. OpenAPI 클라이언트 사용 시, 입력이 올바르게 직렬화되고 출력이 역직렬화되도록 서버와 클라이언트 양측에 동일한 트랜스포머를 구성해야 합니다.

tRPC DataTransformer 인터페이스(serialize/deserialize)를 구현하는 모든 트랜스포머는 configureTRPCHeyApiClient와 호환됩니다. 아래는 테스트된 옵션들입니다.

SuperJSON

TypeScript-to-TypeScript 환경에서 가장 널리 사용되는 트랜스포머입니다. Date, Map, Set, BigInt, RegExp 등을 처리합니다.

bash
pnpm add superjson
bash
pnpm add superjson
src/server.ts
ts
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
const t = initTRPC.create({ transformer: superjson });
src/server.ts
ts
import { initTRPC } from '@trpc/server';
import superjson from 'superjson';
const t = initTRPC.create({ transformer: superjson });
src/client.ts
ts
import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
import superjson from 'superjson';
import { client } from './generated/client.gen';
configureTRPCHeyApiClient(client, {
baseUrl: 'http://localhost:3000',
transformer: superjson,
});
src/client.ts
ts
import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
import superjson from 'superjson';
import { client } from './generated/client.gen';
configureTRPCHeyApiClient(client, {
baseUrl: 'http://localhost:3000',
transformer: superjson,
});

전체 엔드투엔드 예제는 superjson 테스트를 참조하세요.

MongoDB 확장 JSON v2

EJSON는 크로스-랭귀지 지원이 필요할 때 좋은 선택입니다. bson npm 패키지는 tRPC DataTransformer에 직접 매핑되는 EJSON.serialize/EJSON.deserialize를 제공합니다.

지원 언어: C, C#, C++, Go, Java, Node.js, Perl, PHP, Python, Ruby, Scala

bash
pnpm add bson
bash
pnpm add bson
src/transformer.ts
ts
import { EJSON } from 'bson';
import type { TRPCDataTransformer } from '@trpc/server';
export const ejsonTransformer: TRPCDataTransformer = {
serialize: (value) => EJSON.serialize(value),
deserialize: (value) => EJSON.deserialize(value as Document),
};
src/transformer.ts
ts
import { EJSON } from 'bson';
import type { TRPCDataTransformer } from '@trpc/server';
export const ejsonTransformer: TRPCDataTransformer = {
serialize: (value) => EJSON.serialize(value),
deserialize: (value) => EJSON.deserialize(value as Document),
};
src/client.ts
ts
import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
import { client } from './generated/client.gen';
import { ejsonTransformer } from './transformer';
configureTRPCHeyApiClient(client, {
baseUrl: 'http://localhost:3000',
transformer: ejsonTransformer,
});
src/client.ts
ts
import { configureTRPCHeyApiClient } from '@trpc/openapi/heyapi';
import { client } from './generated/client.gen';
import { ejsonTransformer } from './transformer';
configureTRPCHeyApiClient(client, {
baseUrl: 'http://localhost:3000',
transformer: ejsonTransformer,
});

전체 엔드투엔드 예제는 MongoDB EJSON 테스트를 참조하세요.

Amazon Ion

Amazon Ion은 다양한 언어 지원을 갖춘 풍부한 타입의 데이터 포맷입니다. JS/TS에서 tRPC와 직접 작동하도록 TRPCDataTransformer 인터페이스를 지원하지 않으며, 약간의 보일러플레이트 코드가 필요하지만 특정 시스템에 적합할 수 있습니다.

지원 언어: C, C#, D, Go, Java, JavaScript, PHP, Python, Rust

bash
pnpm add ion-js
bash
pnpm add ion-js

트랜스포머 구현, 보일러플레이트 및 전체 엔드투엔드 예제는 Amazon Ion 테스트를 참조하세요.

커스텀 트랜스포머 작성

serializedeserialize 메서드를 가진 모든 객체가 작동합니다:

ts
import type { TRPCDataTransformer } from '@trpc/server';
const myTransformer: TRPCDataTransformer = {
serialize: (value) => {
/* encode rich types */
},
deserialize: (value) => {
/* decode them back */
},
};
ts
import type { TRPCDataTransformer } from '@trpc/server';
const myTransformer: TRPCDataTransformer = {
serialize: (value) => {
/* encode rich types */
},
deserialize: (value) => {
/* decode them back */
},
};

서버에서는 initTRPC.create({ transformer })에, 클라이언트에서는 configureTRPCHeyApiClient(client, { transformer })에 전달하세요. 자세한 내용은 데이터 트랜스포머 문서를 참조하세요.

전체 예제

모든 단계를 통합한 완전한 실행 가능한 프로젝트는 openapi-codegen 예제를 참조하세요.