UNPKG

@trpc/client

Version:

The tRPC client library

160 lines (109 loc) • 8.03 kB
# HTTP Batch Stream Link > A terminating link that batches tRPC calls and streams each response as soon as it is ready From https://trpc.io/docs/client/links/httpBatchStreamLink `httpBatchStreamLink` is a [**terminating link**](https://trpc.io/docs/client/links#the-terminating-link) that batches an array of individual tRPC operations into a single HTTP request that's sent to a single tRPC procedure (equivalent to [`httpBatchLink`](https://trpc.io/docs/client/links/httpBatchLink)), but doesn't wait for all the responses of the batch to be ready and streams the responses as soon as any data is available. ## Options Options are identical to [`httpBatchLink options`](https://trpc.io/docs/client/links/httpBatchLink#options), with the following addition: | Option | Type | Default | Description | | -------------- | ----------------------------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `streamHeader` | `'trpc-accept'` \| `'accept'` | `'trpc-accept'` | Which header to use to signal the server that the client wants a streaming response. `'accept'` uses the standard `Accept` header instead of the custom `trpc-accept` header, which can avoid CORS preflight for cross-origin streaming queries since `Accept` is a CORS-safelisted header. | ## Usage > All usage and options are identical to [`httpBatchLink`](https://trpc.io/docs/client/links/httpBatchLink). > [!NOTE] > If you require the ability to change/set response headers (which includes cookies) from within your procedures, make sure to use `httpBatchLink` instead! This is due to the fact that `httpBatchStreamLink` does not support setting headers once the stream has begun. [Read more](https://trpc.io/docs/client/links/httpBatchLink). You can import and add the `httpBatchStreamLink` to the `links` array as such: ```ts no-check title="client/index.ts" import { createTRPCClient, httpBatchStreamLink } from '@trpc/client'; import type { AppRouter } from './server'; const client = createTRPCClient<AppRouter>({ links: [ httpBatchStreamLink({ url: 'http://localhost:3000', }), ], }); ``` After that, you can make use of batching by setting all your procedures in a `Promise.all`. The code below will produce exactly **one** HTTP request and on the server exactly **one** database query: ```ts no-check const somePosts = await Promise.all([ trpc.post.byId.query(1), trpc.post.byId.query(2), trpc.post.byId.query(3), ]); ``` ## Streaming mode When batching requests together, the behavior of a regular `httpBatchLink` is to wait for all requests to finish before sending the response. If you want to send responses as soon as they are ready, you can use `httpBatchStreamLink` instead. This is useful for long-running requests. ```ts no-check title="client/index.ts" import { createTRPCClient, httpBatchStreamLink } from '@trpc/client'; import type { AppRouter } from './server'; const client = createTRPCClient<AppRouter>({ links: [ httpBatchStreamLink({ url: 'http://localhost:3000', }), ], }); ``` Compared to a regular `httpBatchLink`, a `httpBatchStreamLink` will: - Cause the requests to be sent with a `trpc-accept: application/jsonl` header (or `Accept: application/jsonl` when using `streamHeader: 'accept'`) - Cause the response to be sent with `transfer-encoding: chunked` and `content-type: application/json` by default. You can set the streamed response `content-type` to `application/jsonl`, `application/x-ndjson`, or another value on the server via `initTRPC.create({ jsonl: { contentType: ... } })`. - Remove the `data` key from the argument object passed to `responseMeta` (because with a streamed response, the headers are sent before the data is available) ## Async generators and deferred promises You can try this out on the homepage of tRPC.io: [https://trpc.io/?try=minimal#try-it-out](https://trpc.io/?try=minimal#try-it-out) ```ts no-check 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(); for await (const value of iterable) { console.log('Iterable:', value); } ``` ## Compatibility (client-side) ### Browsers Browser support should be identical to [`fetch`](https://caniuse.com/fetch) support. ### Node.js / Deno For runtimes other than the browser ones, the `fetch` implementation should support streaming, meaning that the response obtained by `await fetch(...)` should have a `body` property of type `ReadableStream<Uint8Array> | NodeJS.ReadableStream`, meaning that: - either `response.body.getReader` is a function that returns a `ReadableStreamDefaultReader<Uint8Array>` object - or `response.body` is a `Uint8Array` `Buffer` This includes support for `undici`, `node-fetch`, native Node.js fetch implementation, and WebAPI fetch implementation (browsers). ### React Native Receiving the stream relies on the `TextDecoder` and `TextDecoderStream` APIs, which are not available in React Native. It's important to note that if your `TextDecoderStream` polyfill does not automatically polyfill `ReadableStream` and `WritableStream` those will also need to be polyfilled. If you still want to enable streaming, you need to polyfill those. You will also need to override the default fetch in the `httpBatchStreamLink` configuration options with one that can stream. In the below example we will be using [`react-native-fetch-api`](https://github.com/react-native-community/fetch), which streams text when you pass its `reactNative` option. On Expo, you can pass `fetch` from [`expo/fetch`](https://docs.expo.dev/versions/latest/sdk/expo/) instead, which streams without extra options. ```ts import { httpBatchStreamLink } from '@trpc/client'; import { fetch } from 'react-native-fetch-api'; httpBatchStreamLink({ fetch: (url, opts) => fetch(url, { ...opts, reactNative: { textStreaming: true }, }), url: 'http://localhost:3000', }); ``` ## Compatibility (server-side) > [!WARNING] AWS Lambda > `httpBatchStreamLink` only supported on AWS Lambda when your [infrastructure is set up](https://trpc.io/docs/server/adapters/aws-lambda#aws-lambda-response-streaming-adapter) for streaming responses. If not this Link will simply behave like a regular `httpBatchLink`. > [!WARNING] Cloudflare Workers > You need to enable the `ReadableStream` API through a feature flag: [`streams_enable_constructors`](https://developers.cloudflare.com/workers/platform/compatibility-dates#streams-constructors). ## Reference You can check out the source code for this link on [GitHub.](https://github.com/trpc/trpc/blob/main/packages/client/src/links/httpBatchStreamLink.ts) ## Configure JSONL streaming options When setting up your root config, you can pass in a `jsonl` option to configure ping behavior and the response `content-type` for streamed batch responses. ```ts no-check import { initTRPC } from '@trpc/server'; const t = initTRPC.create({ jsonl: { pingMs: 1000, contentType: 'application/jsonl', }, }); ``` The default `contentType` remains `application/json` for backwards compatibility. Use `application/jsonl` if you want the response header to match the `trpc-accept` / `Accept` header sent by `httpBatchStreamLink`, or choose another value that works better with your tooling.