メインコンテンツへスキップ

tRPC v11の発表

· 1分で読める
tRPC
tRPCチーム
非公式ベータ版翻訳

このページは PageTurner AI で翻訳されました(ベータ版)。プロジェクト公式の承認はありません。 エラーを見つけましたか? 問題を報告 →

tRPC v11は長らく@nextタグを通じてプロダクション環境で使用可能でしたが、私たちはセマンティックバージョニングに厳密にならずに新機能を追加することに夢中になってしまいました。ついに今日、私たちはこのバンドエイドをはがす決断をし、tRPC v11の正式リリースを発表できることを大変嬉しく思います!

前回のメジャーバージョンリリース(2022年11月)以来、tRPCコミュニティは大幅な成長を遂げています:

tRPC v11のローンチに際し、@nextチャンネルでの安定した進化のおかげで、すでに多くの大規模TypeScriptプロジェクトが本番環境でv11を採用していることを共有できることを嬉しく思います。

新規プロジェクトではtRPC v11を学べるサンプルアプリケーションで始められます。tRPC v10を使用中のプロジェクトはv11移行ガイドをご参照ください。

変更点の概要

v11はv10との後方互換性をほぼ維持しつつ、多くの新機能と改善が導入されています。主なハイライトは以下の通りです:

TanStack Query v5サポート

TanStack Query v5のリリースに伴い、tRPCのreact-query統合部分に破壊的変更が導入されました。この変更は@nextタグで早期から利用可能でしたが、今回正式リリースとなりました。多くのプロジェクトがすでにアップグレードを完了し、React Suspenseの完全サポートなど多くの改善を享受しています。tRPC Reactクライアントコードの移行については、TanStack Query公式移行ガイドをご参照ください。

新しいTanStack React Query統合

ブログ記事を参照

FormData / 非JSONコンテンツタイプのサポート

最も要望の多かった機能の一つであるJSON以外のデータ送受信が可能になりました。tRPC v11では、様々な非JSONコンテンツタイプFormDataBlobFileUint8Arrayなどのバイナリタイプを送受信できるようになりました。これらのコンテンツタイプの使用方法はこちらのサンプルで確認できます。

ts
import { publicProcedure, router } from './trpc';
import { octetInputParser } from '@trpc/server/http';
import { z } from 'zod';
 
const appRouter = router({
formData: publicProcedure
.input(z.instanceof(FormData))
.mutation(async ({ input }) => {
(parameter) input: FormData
}),
file: publicProcedure
.input(octetInputParser)
.mutation(async ({ input }) => {
(parameter) input: ReadableStream<any>
}),
});
ts
import { publicProcedure, router } from './trpc';
import { octetInputParser } from '@trpc/server/http';
import { z } from 'zod';
 
const appRouter = router({
formData: publicProcedure
.input(z.instanceof(FormData))
.mutation(async ({ input }) => {
(parameter) input: FormData
}),
file: publicProcedure
.input(octetInputParser)
.mutation(async ({ input }) => {
(parameter) input: ReadableStream<any>
}),
});

React Server Components / Next.js App Router

Next.js App RouterとtRPCの組み合わせは初日から可能でしたが、主に以下の二つのアプローチがあります:

この間をつなぐ部分には若干の課題がありました。異なるデータ取得パターンを混在させることで再検証パターンが複雑化し、tRPCが目指す理想的な開発者体験には届かない状態でした。

この問題を解決するため、React Server Components(RSC)のサポートを強化し、サーバー上で排他的に実行されるRSCの能力とReact Queryの高度なクライアントサイドキャッシュを組み合わせやすくするプリフェッチヘルパーを追加しました。これにより、サーバー上のRSCでプロシージャの実行を開始し、クライアント側で保留中のPromiseを引き継ぎ、React Queryキャッシュを自動的にハイドレートできるようになりました。これによってサーバーとクライアント間のウォーターフォールに悩まされることなく、高度に動的なアプリケーションを構築できます。詳細はServer Componentsのドキュメントをご覧ください。

新しいプリフェッチパターンに加え、サーバー関数の実験的サポートも追加しました。詳細は当社のブログ記事でご確認いただけます。サーバー関数がエコシステムでより確立されたパターンになるにつれ、この機能の改良を続けていく予定です。

またTanStackチームと協力してサーバー関数のAPI設計にも取り組みました。tRPCの強力なミドルウェアシステムの一部を切り出し、エコシステム全体で利用可能な別パッケージとすることを目指しています。

クエリとミューテーションからのストリーミング応答

クエリ応答をストリーミング可能なhttpBatchStreamLinkを導入しました。大規模データセットを扱う場合や、データをリアルタイムで処理しながらフロントエンドに送信する必要がある場合に有用です。これはサブスクリプションの代替ではなく、新たな選択肢としてご活用ください。

ts
// @filename: server.ts
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
examples: {
iterable: publicProcedure.query(async function* () {
let i = 0;
while (true) {
await new Promise((resolve) => setTimeout(resolve, 500));
yield i++;
}
}),
},
});
 
export type AppRouter = typeof appRouter;
 
 
// @filename: client.ts
import { createTRPCClient, httpBatchStreamLink } from '@trpc/client';
import type { AppRouter } from './server';
 
const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchStreamLink({
url: 'http://localhost:3000',
}),
],
});
const iterable = await trpc.examples.iterable.query();
const iterable: AsyncIterable<number, never, unknown>
 
for await (const value of iterable) {
console.log('Iterable:', value);
const value: number
}
ts
// @filename: server.ts
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
examples: {
iterable: publicProcedure.query(async function* () {
let i = 0;
while (true) {
await new Promise((resolve) => setTimeout(resolve, 500));
yield i++;
}
}),
},
});
 
export type AppRouter = typeof appRouter;
 
 
// @filename: client.ts
import { createTRPCClient, httpBatchStreamLink } from '@trpc/client';
import type { AppRouter } from './server';
 
const trpc = createTRPCClient<AppRouter>({
links: [
httpBatchStreamLink({
url: 'http://localhost:3000',
}),
],
});
const iterable = await trpc.examples.iterable.query();
const iterable: AsyncIterable<number, never, unknown>
 
for await (const value of iterable) {
console.log('Iterable:', value);
const value: number
}

ルーター定義の短縮構文

ルート定義プロセスを簡素化するため、ルーター定義の新しい短縮構文を導入しました。ドキュメント

ts
const appRouter = router({
// Shorthand plain object for creating a sub-router
nested1: {
proc: publicProcedure.query(() => '...'),
},
// Equivalent of:
nested2: router({
proc: publicProcedure.query(() => '...'),
}),
});
ts
const appRouter = router({
// Shorthand plain object for creating a sub-router
nested1: {
proc: publicProcedure.query(() => '...'),
},
// Equivalent of:
nested2: router({
proc: publicProcedure.query(() => '...'),
}),
});

サブスクリプション:Server-Sent Eventsとその他の改良

  • tRPC v11ではServer-Sent Events(SSE)を利用した新たなサブスクリプション処理方法を導入しました。複雑なWebSocketを必要とせずリアルタイム更新を実現する優れた方法です。今後はまずこの方法をお試しください。

  • サブスクリプションにおけるJavaScriptジェネレーターのサポートを追加。時間経過とともに複数の値を生成し、終了時にクリーンアップできるより複雑なサブスクリプションハンドラーの記述が、JavaScriptらしい方法で可能になりました。

  • サブスクリプションで出力バリデーションをサポートし、サブスクリプションハンドラーの型安全性を向上させました。

v9 .interop()モードの終焉

tRPC v10ではv9ユーザーのスムーズな移行を支援するため.interop()モードを導入しましたが、v11では.interop()モードを削除しました。.interop()モードをまだ使用されている場合はv10移行ガイドを参照し、現在のtRPC APIへの移行を完了してください。

v11への移行

現在tRPC v10をご使用の場合、移行ガイドに従ってv11にアップグレードできます。移行ガイドではv11の全破壊的変更と新機能を網羅しています。

感謝を込めて

tRPCコアチーム一同、tRPCをご利用・ご支援いただいている皆様に感謝申し上げます。