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

콘텐츠 타입

tRPC는 프로시저 입력으로 여러 콘텐츠 타입을 지원합니다: JSON 직렬화 가능한 데이터, FormData, File, Blob 및 기타 바이너리 타입.

JSON (기본값)

기본적으로 tRPC는 JSON 직렬화 가능한 데이터를 주고받습니다. 추가 구성이 필요하지 않으며, JSON으로 직렬화할 수 있는 모든 입력은 모든 링크(httpLink, httpBatchLink, httpBatchStreamLink)에서 즉시 작동합니다.

ts
import { z } from 'zod';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure.input(z.object({ name: z.string() })).query((opts) => {
return { greeting: `Hello ${opts.input.name}` };
}),
});
ts
import { z } from 'zod';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure.input(z.object({ name: z.string() })).query((opts) => {
return { greeting: `Hello ${opts.input.name}` };
}),
});

비-JSON 콘텐츠 타입

JSON 외에도 tRPC는 FormData, File 및 기타 바이너리 타입을 프로시저 입력으로 사용할 수 있습니다.

클라이언트 설정

정보

tRPC는 여러 비-JSON 직렬화 가능 타입을 네이티브로 지원하지만, 설정에 따라 이러한 타입을 지원하기 위해 클라이언트에 일부 링크 구성이 필요할 수 있습니다.

httpLink는 비-JSON 콘텐츠 타입을 즉시 지원합니다 — 이 링크만 사용하는 경우, 기존 설정이 바로 작동해야 합니다.

ts
import { createTRPCClient, httpLink } from '@trpc/client';
import type { AppRouter } from './server';
 
createTRPCClient<AppRouter>({
links: [
httpLink({
url: 'http://localhost:2022',
}),
],
});
ts
import { createTRPCClient, httpLink } from '@trpc/client';
import type { AppRouter } from './server';
 
createTRPCClient<AppRouter>({
links: [
httpLink({
url: 'http://localhost:2022',
}),
],
});

그러나 모든 링크가 이러한 콘텐츠 타입을 지원하지는 않습니다. httpBatchLink 또는 httpBatchStreamLink를 사용하는 경우, splitLink를 포함하고 콘텐츠 타입에 따라 요청을 라우팅해야 합니다.

ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
}),
false: httpBatchLink({
url,
}),
}),
],
});
ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
}),
false: httpBatchLink({
url,
}),
}),
],
});

tRPC 서버에서 transformer를 사용하는 경우, TypeScript는 tRPC 클라이언트 링크에서도 transformer를 정의하도록 요구합니다. 이 예시를 기반으로 사용하세요:

ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import superjson from 'superjson';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
transformer: {
// request - convert data before sending to the tRPC server
serialize: (data) => data,
// response - convert the tRPC response before using it in client
deserialize: (data) => superjson.deserialize(data), // or your other transformer
},
}),
false: httpBatchLink({
url,
transformer: superjson, // or your other transformer
}),
}),
],
});
ts
import {
createTRPCClient,
httpBatchLink,
httpLink,
isNonJsonSerializable,
splitLink,
} from '@trpc/client';
import superjson from 'superjson';
import type { AppRouter } from './server';
 
const url = 'http://localhost:2022';
 
createTRPCClient<AppRouter>({
links: [
splitLink({
condition: (op) => isNonJsonSerializable(op.input),
true: httpLink({
url,
transformer: {
// request - convert data before sending to the tRPC server
serialize: (data) => data,
// response - convert the tRPC response before using it in client
deserialize: (data) => superjson.deserialize(data), // or your other transformer
},
}),
false: httpBatchLink({
url,
transformer: superjson, // or your other transformer
}),
}),
],
});

서버 설정

정보

요청이 tRPC에 의해 처리되면, 요청의 Content-Type 헤더에 따라 요청 본문을 파싱하는 작업을 담당합니다.
Failed to parse body as XXX와 같은 오류가 발생하는 경우, tRPC가 처리하기 전에 서버(예: Express, Next.js)가 요청 본문을 파싱하지 않도록 확인하세요.

Twoslash failure

Errors were thrown in the sample, but not included in an errors tag

These errors were not marked as being expected: 7006.
Expected: // @errors: 7006

Compiler Errors:

app.ts
[7006] 300 - Parameter 'req' implicitly has an 'any' type.
[7006] 305 - Parameter 'res' implicitly has an 'any' type.
[7006] 607 - Parameter 'req' implicitly has an 'any' type.
[7006] 612 - Parameter 'res' implicitly has an 'any' type.

Raising Code:

// @filename: router.ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create();
export const appRouter = t.router({});

// @filename: app.ts
// ---cut---
// Example in express
import express from 'express';
import * as trpcExpress from '@trpc/server/adapters/express';
import { appRouter } from './router';

// incorrect
const app1 = express();
app1.use(express.json()); // this tries to parse body before tRPC.
app1.post('/express/hello', (req, res) => { res.end(); }); // normal express route handler
app1.use('/trpc', trpcExpress.createExpressMiddleware({ router: appRouter })); // tRPC fails to parse body

// correct
const app2 = express();
app2.use('/express', express.json()); // do it only in "/express/*" path
app2.post('/express/hello', (req, res) => { res.end(); });
app2.use('/trpc', trpcExpress.createExpressMiddleware({ router: appRouter })); // tRPC can parse body

FormData 입력

FormData는 네이티브로 지원되며, 더 고급 사용 사례에서는 zod-form-data와 같은 라이브러리를 결합하여 타입 안전하게 입력을 검증할 수 있습니다.

ts
import { z } from 'zod';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure.input(z.instanceof(FormData)).mutation((opts) => {
const data = opts.input;
const data: FormData
return {
greeting: `Hello ${data.get('name')}`,
};
}),
});
ts
import { z } from 'zod';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
hello: publicProcedure.input(z.instanceof(FormData)).mutation((opts) => {
const data = opts.input;
const data: FormData
return {
greeting: `Hello ${data.get('name')}`,
};
}),
});

더 고급 코드 샘플은 여기서 예제 프로젝트를 확인하세요.

File 및 기타 바이너리 타입 입력

tRPC는 많은 octet 콘텐츠 타입을 프로시저에서 소비할 수 있는 ReadableStream로 변환합니다. 현재 지원되는 타입은 Blob, Uint8ArrayFile입니다.

ts
import { octetInputParser } from '@trpc/server/http';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
upload: publicProcedure.input(octetInputParser).mutation((opts) => {
const data = opts.input;
const data: ReadableStream<any>
return {
valid: true,
};
}),
});
ts
import { octetInputParser } from '@trpc/server/http';
 
export const t = initTRPC.create();
const publicProcedure = t.procedure;
 
export const appRouter = t.router({
upload: publicProcedure.input(octetInputParser).mutation((opts) => {
const data = opts.input;
const data: ReadableStream<any>
return {
valid: true,
};
}),
});