メインコンテンツへスキップ
バージョン: 11.x

HTTPバッチストリームリンク

非公式ベータ版翻訳

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

httpBatchStreamLink終端リンクであり、個々のtRPC操作の配列を単一のHTTPリクエストにバッチ処理し(httpBatchLinkと同等)、単一のtRPCプロシージャに送信します。ただし、バッチ内のすべてのレスポンスの準備が整うのを待たず、データが利用可能になるとすぐにレスポンスをストリーミングします。

オプション

オプションはhttpBatchLink optionsと同一です。

使用方法

使用方法とオプションはすべてhttpBatchLinkと同一です。

注記

プロシージャ内からレスポンスヘッダー(Cookieを含む)を変更/設定する機能が必要な場合は、代わりにhttpBatchLinkを使用してください!これは、httpBatchStreamLinkがストリーム開始後のヘッダー設定をサポートしないためです。詳細はこちら

httpBatchStreamLinkをインポートしてlinks配列に追加する例:

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

その後、すべてのプロシージャをPromise.allで設定することでバッチ処理を活用できます。以下のコードは単一のHTTPリクエストを生成し、サーバー側では単一のデータベースクエリを実行します:

ts
const somePosts = await Promise.all([
trpc.post.byId.query(1),
trpc.post.byId.query(2),
trpc.post.byId.query(3),
]);
ts
const somePosts = await Promise.all([
trpc.post.byId.query(1),
trpc.post.byId.query(2),
trpc.post.byId.query(3),
]);

ストリーミングモード

リクエストをバッチ処理する際、通常のhttpBatchLinkはすべてのリクエストが完了するまでレスポンスを送信しません。レスポンスの準備が整い次第すぐに送信したい場合は、代わりにhttpBatchStreamLinkを使用できます。これは長時間実行されるリクエストに有効です。

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

通常のhttpBatchLinkと比較して、httpBatchStreamLinkは以下の挙動を示します:

  • リクエストにtrpc-accept: application/jsonlヘッダーを付与する

  • レスポンスをtransfer-encoding: chunkedおよびcontent-type: application/jsonlで送信する

  • responseMetaに渡される引数オブジェクトからdataキーを削除する(ストリーミングレスポンスではヘッダーがデータ利用前に送信されるため)

非同期ジェネレータと遅延プロミス

tRPC.ioのホームページで実際に試すことができます:https://trpc.io/?try=minimal#try-it-out

ts
// @filename: server.ts
import { publicProcedure, router } from './trpc';
 
const appRouter = router({
examples: {
iterable: publicProcedure.query(async function* () {
for (let i = 0; i < 3; i++) {
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* () {
for (let i = 0; i < 3; i++) {
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
}

互換性(クライアント側)

ブラウザ

ブラウザサポートはfetchのサポートと同一である必要があります。

Node.js / Deno

ブラウザ以外のランタイムでは、fetch実装がストリーミングをサポートしている必要があります。つまりawait fetch(...)で得られるレスポンスのbodyプロパティがReadableStream<Uint8Array> | NodeJS.ReadableStream型であることを意味し、以下いずれかの条件を満たします:

  • response.body.getReaderReadableStreamDefaultReader<Uint8Array>オブジェクトを返す関数である

  • またはresponse.bodyUint8ArrayBufferである

これにはundicinode-fetch、ネイティブNode.js fetch実装、WebAPI fetch実装(ブラウザ)のサポートが含まれます。

React Native

ストリームの受信にはTextDecoderおよびTextDecoderStream APIが必要ですが、React Nativeでは利用できません。重要な注意点として、TextDecoderStreamのポリフィルが自動的にReadableStreamおよびWritableStreamをポリフィルしない場合、これらも追加でポリフィルする必要があります。ストリーミングを有効化するにはこれらのポリフィルが必須です。

また、httpBatchStreamLinkの設定オプションでデフォルトのfetchをオーバーライドする必要があります。以下の例では、fetchの実装にExpo fetchパッケージを使用します。

typescript
httpBatchStreamLink({
fetch: (url, opts) =>
fetch(url, {
...opts,
reactNative: { textStreaming: true },
}),
...restOfConfig,
});
typescript
httpBatchStreamLink({
fetch: (url, opts) =>
fetch(url, {
...opts,
reactNative: { textStreaming: true },
}),
...restOfConfig,
});

互換性(サーバーサイド)

⚠️ AWS Lambdaでは、httpBatchStreamLinkはサポートされていません(通常のhttpBatchLinkのように動作します)。有効にしても問題は発生しませんが、効果はありません。

⚠️ Cloudflare Workersでは、ReadableStream APIを機能フラグstreams_enable_constructorsを通じて有効にする必要があります。

参考

このリンクのソースコードはGitHubで確認できます。

接続を維持するためのpingオプションの設定

ルート設定を行う際に、jsonlオプションを渡して接続を維持するためのpingオプションを設定できます。

ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create({
jsonl: {
pingMs: 1000,
},
});
ts
import { initTRPC } from '@trpc/server';
const t = initTRPC.create({
jsonl: {
pingMs: 1000,
},
});