본문으로 건너뛰기
버전: 11.x

OpenAPI (알파)

주의

이 패키지는 알파 버전입니다. 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
옵션기본값설명
-e, --export <name>AppRouter내보낸 라우터 타입의 이름
-o, --output <file>openapi.json출력 파일 경로
--title <text>tRPC APIOpenAPI info.title
--version <ver>0.0.0OpenAPI info.version
--server-url <url>기본 URL(접두어 포함), 예: https://api.example.com/trpc
bash
pnpm exec trpc-openapi ./src/server/router.ts -o api.json --title "My API" --version 1.0.0 --server-url https://api.example.com/trpc
bash
pnpm exec trpc-openapi ./src/server/router.ts -o api.json --title "My API" --version 1.0.0 --server-url https://api.example.com/trpc

프로그래매틱​

scripts/generate-openapi.ts
ts
import { generateOpenAPIDocument } from '@trpc/openapi';
const doc = await generateOpenAPIDocument('./src/server/router.ts', {
exportName: 'AppRouter',
title: 'My API',
version: '1.0.0',
servers: [{ url: 'https://api.example.com/trpc' }],
});
scripts/generate-openapi.ts
ts
import { generateOpenAPIDocument } from '@trpc/openapi';
const doc = await generateOpenAPIDocument('./src/server/router.ts', {
exportName: 'AppRouter',
title: 'My API',
version: '1.0.0',
servers: [{ url: 'https://api.example.com/trpc' }],
});

생성기는 라우터의 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 Extended 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은 광범위한 언어 지원을 갖춘 풍부한 타입의 데이터 형식입니다. TRPCDataTransformer 인터페이스를 직접 지원하지 않으며 JS/TS에서 tRPC와 함께 작동하려면 약간의 보일러플레이트가 필요하지만, 자체 시스템에 좋은 선택이 될 수 있습니다.

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

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

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

커스텀 트랜스포머 작성​

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

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 예제를 확인하세요.

oasdiff를 사용한 API 변경 로그 및 비호환 변경 검사​

OpenAPI 스펙을 생성한 후, oasdiff를 사용하여 두 버전을 비교할 수 있습니다.

설치 옵션(Homebrew, Docker, 바이너리 등)에 대한 자세한 내용은 공식 문서를 확인하세요:

이를 사용하면 변경 로그를 빠르게 생성하고 의도하지 않은 API 비호환 변경을 감지하여 릴리스를 계획하고 조정할 수 있습니다.

sh
# Get a complete changelog including minor and breaking changes
$ oasdiff changelog packages/openapi/test/routers/superjsonRouter.openapi.json /tmp/superjsonRouter.openapi.next.json
3 changes: 2 error, 0 warning, 1 info
error [new-required-request-property] in API POST /createEvent
added the new required request property 'location'
error [api-path-removed-without-deprecation] in API GET /getBigInt
api path removed without deprecation
info [endpoint-added] in API GET /health
endpoint added
# Get a list of breaking changes if any
$ oasdiff breaking packages/openapi/test/routers/superjsonRouter.openapi.json /tmp/superjsonRouter.openapi.next.json
2 changes: 2 error, 0 warning, 0 info
error [new-required-request-property] in API POST /createEvent
added the new required request property 'location'
error [api-path-removed-without-deprecation] in API GET /getBigInt
api path removed without deprecation
sh
# Get a complete changelog including minor and breaking changes
$ oasdiff changelog packages/openapi/test/routers/superjsonRouter.openapi.json /tmp/superjsonRouter.openapi.next.json
3 changes: 2 error, 0 warning, 1 info
error [new-required-request-property] in API POST /createEvent
added the new required request property 'location'
error [api-path-removed-without-deprecation] in API GET /getBigInt
api path removed without deprecation
info [endpoint-added] in API GET /health
endpoint added
# Get a list of breaking changes if any
$ oasdiff breaking packages/openapi/test/routers/superjsonRouter.openapi.json /tmp/superjsonRouter.openapi.next.json
2 changes: 2 error, 0 warning, 0 info
error [new-required-request-property] in API POST /createEvent
added the new required request property 'location'
error [api-path-removed-without-deprecation] in API GET /getBigInt
api path removed without deprecation