refactor(rpc): export batch constants and improve documentation (#2290) · middleapi/orpc@7604f11 · GitHub
Skip to content

Commit 7604f11

Browse files
dinwwwhclaude
andauthored
refactor(rpc): export batch constants and improve documentation (#2290)
## Summary Extracts batch-related constants (`BATCH_HEADER` and `DEFAULT_BATCH_MAX_SIZE`) into shared exports and improves documentation around batch request handling, particularly regarding header preservation and size limits. ## Key Changes - **New exports**: Added `BATCH_HEADER` ('orpc-batch') and `DEFAULT_BATCH_MAX_SIZE` (10) constants to `@orpc/client/plugins` and re-exported from `@orpc/server/plugins` for consistency across client and server - **Updated batch plugins**: Both `BatchLinkPlugin` and `BatchHandlerPlugin` now use the exported constants instead of hardcoded values - **Improved documentation**: - Clarified the warning about batch header preservation (CORS, proxies, etc.) - Added new "Max Size" section with code examples showing how to configure `maxSize` on both client and server - Enhanced JSDoc comments explaining the relationship between client and server `maxSize` limits and the 413 rejection behavior - **Consistent header handling**: Updated all references to the batch header string to use the `BATCH_HEADER` constant across client and server implementations - **Test updates**: Added assertions verifying the new constants are properly exported ## Implementation Details The constants are defined in `@orpc/client/plugins/batch.ts` and re-exported from the server plugins index, ensuring a single source of truth. This makes it easier for users to reference the exact header name and default size limit in their configurations. https://claude.ai/code/session_01RmF7F7emDsPDYH5ZGrUumt <!-- Macroscope's pull request summary starts here --> <!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. --> <!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. --> > [!NOTE] > ### Export `BATCH_HEADER` and `DEFAULT_BATCH_MAX_SIZE` constants for batch plugins > - Adds exported shared constants `BATCH_HEADER` ("orpc-batch") and `DEFAULT_BATCH_MAX_SIZE` (10) in the client plugins module, and re-exports them from the server plugins entrypoint. Client and server batch plugins now use these constants instead of inline literals. Values are unchanged. > - Updates `maxSize` docs on both `BatchLinkPluginOptions` and `BatchHandlerPluginOptions` to explain client splitting and the server `413` response, and rewrites [batch.mdx](https://github.com/middleapi/orpc/pull/2290/files#diff-c0c938d06b5d63227600731a917541aeb24b4901a7d05eb20c550dc142290238) with a Max Size section and clarified `BATCH_HEADER` warning. > - Extends plugin export tests in the client and server packages to assert the new constants. > - Behavioral Change: none — default batch size stays 10 and the header name stays "orpc-batch"; this only moves them to shared constants. > > <!-- Macroscope's review summary starts here --> > > <sup><a href="https://app.macroscope.com">Macroscope</a> summarized 1bd08c8.</sup> > <!-- Macroscope's review summary ends here --> > <!-- Macroscope's pull request summary ends here --> --------- Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0a6444f commit 7604f11

7 files changed

Lines changed: 69 additions & 16 deletions

File tree

‎apps/content/docs/plugins/batch.mdx‎

Lines changed: 33 additions & 5 deletions

‎packages/client/src/plugins/batch.ts‎

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,21 @@ import { captureAsyncContext, defer, isAsyncIteratorObject, loadBytes, once, pro
77
import { parseStandardUrl } from '@standard-server/core'
88
import { ClientPeer, decodePeerMessage, isServerPeerSendMessage } from '@standard-server/peer'
99

10+
/**
11+
* Header that marks a request as a batch, set by the Batch Link Plugin and detected by the Batch Handler Plugin.
12+
* Nothing between the client and server, such as a CORS policy or proxy, should block or strip it.
13+
*
14+
* @see {@link https://orpc.dev/docs/plugins/batch#setup | Batch Plugin - Setup}
15+
*/
16+
export const BATCH_HEADER = 'orpc-batch'
17+
18+
/**
19+
* Default `maxSize` of both the Batch Link Plugin and the Batch Handler Plugin.
20+
*
21+
* @see {@link https://orpc.dev/docs/plugins/batch#max-size | Batch Plugin - Max Size}
22+
*/
23+
export const DEFAULT_BATCH_MAX_SIZE = 10
24+
1025
export interface BatchLinkPluginGroup<T extends ClientContext> {
1126
/**
1227
* Determines whether a request should be included in this batch group.
@@ -48,9 +63,11 @@ export interface BatchLinkPluginOptions<T extends ClientContext> {
4863
scope?: ((options: StandardLinkTransportInterceptorOptions<T>) => unknown) | undefined
4964

5065
/**
51-
* The maximum number of requests in the batch.
66+
* The maximum number of requests in the batch. Larger batches are split.
67+
* Keep it at or below the server Batch Handler Plugin's `maxSize`, which rejects larger batches with a `413`.
5268
*
53-
* @default 10
69+
* @default DEFAULT_BATCH_MAX_SIZE (10)
70+
* @see {@link https://orpc.dev/docs/plugins/batch#max-size | Batch Plugin - Max Size}
5471
*/
5572
maxSize?: Value<Promisable<number>, [subOptionsList: [StandardLinkTransportInterceptorOptions<T>, ...StandardLinkTransportInterceptorOptions<T>[]]]> | undefined
5673

@@ -131,7 +148,7 @@ export class BatchLinkPlugin<T extends ClientContext> implements StandardLinkPlu
131148
this.groups = options.groups
132149
this.filter = options.filter ?? (() => true)
133150
this.scope = options.scope ?? (() => undefined)
134-
this.maxSize = options.maxSize ?? 10
151+
this.maxSize = options.maxSize ?? DEFAULT_BATCH_MAX_SIZE
135152
this.wait = options.wait ?? 0
136153
this.batchUrl = options.url ?? ((options) => {
137154
const [pathname] = parseStandardUrl(options[0].request.url)
@@ -357,7 +374,7 @@ export class BatchLinkPlugin<T extends ClientContext> implements StandardLinkPlu
357374
const request: StandardRequest = {
358375
url,
359376
method,
360-
headers: { ...headers, 'orpc-batch': '1' },
377+
headers: { ...headers, [BATCH_HEADER]: '1' },
361378
signal: controller.signal,
362379
}
363380

‎packages/client/src/plugins/index.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
it('exports plugins', async () => {
22
await expect(import('./index')).resolves.toMatchObject({
33
BatchLinkPlugin: expect.any(Function),
4+
BATCH_HEADER: 'orpc-batch',
5+
DEFAULT_BATCH_MAX_SIZE: 10,
46
DedupeLinkPlugin: expect.any(Function),
57
RetryLinkPlugin: expect.any(Function),
68
RetryAfterLinkPlugin: expect.any(Function),

‎packages/node/src/batch-response-compression-handler-plugin.ts‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import type { StandardHandlerOptions, StandardHandlerPlugin, StandardHandlerRout
33
import type { StandardBodyHint, StandardHeaders } from '@standard-server/core'
44
import { Duplex } from 'node:stream'
55
import { constants, createDeflate, createDeflateRaw, createGzip } from 'node:zlib'
6-
import { negotiateResponseCompressionEncoding, toCompressedHeaders } from '@orpc/server/plugins'
6+
import { BATCH_HEADER, negotiateResponseCompressionEncoding, toCompressedHeaders } from '@orpc/server/plugins'
77
import { stringifyJSON, toArray } from '@orpc/shared'
88
import { flattenStandardHeader } from '@standard-server/core'
99

@@ -71,7 +71,7 @@ export class BatchResponseCompressionHandlerPlugin<T extends Context> implements
7171
* a batch without it belongs to a procedure serving that content type itself, whose own file
7272
* semantics must survive untouched.
7373
*/
74-
if (flattenStandardHeader(interceptorOptions.request.headers['orpc-batch']) === undefined) {
74+
if (flattenStandardHeader(interceptorOptions.request.headers[BATCH_HEADER]) === undefined) {
7575
return result
7676
}
7777

‎packages/server/src/plugins/batch.ts‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import type { ClientPeerSendMessage, ServerPeerSendMessage } from '@standard-ser
44
import type { StandardHandlerOptions, StandardHandlerPlugin, StandardHandlerRoutingInterceptor, StandardHandlerRoutingInterceptorOptions } from '../adapters/standard'
55
import type { Context } from '../context'
66
import { ORPCError } from '@orpc/client'
7+
import { BATCH_HEADER, DEFAULT_BATCH_MAX_SIZE } from '@orpc/client/plugins'
78
import { isAsyncIteratorObject, promiseWithResolvers, toArray, value } from '@orpc/shared'
89
import { flattenStandardHeader, parseStandardUrl } from '@standard-server/core'
910
import { encodePeerMessage, isClientPeerSendMessage, ServerPeer } from '@standard-server/peer'
@@ -114,9 +115,11 @@ function liftSubresponseHeaders(
114115

115116
export interface BatchHandlerPluginOptions<T extends Context> {
116117
/**
117-
* The max size of the batch allowed.
118+
* The max size of the batch allowed. Larger batches are rejected with a `413`,
119+
* so keep the client Batch Link Plugin's `maxSize` at or below it.
118120
*
119-
* @default 10
121+
* @default DEFAULT_BATCH_MAX_SIZE (10)
122+
* @see {@link https://orpc.dev/docs/plugins/batch#max-size | Batch Plugin - Max Size}
120123
*/
121124
maxSize?: Value<Promisable<number>, [options: StandardHandlerRoutingInterceptorOptions<T>]> | undefined
122125

@@ -211,7 +214,7 @@ export class BatchHandlerPlugin<T extends Context> implements StandardHandlerPlu
211214
private readonly keepAliveInterval: number
212215

213216
constructor(options: BatchHandlerPluginOptions<T> = {}) {
214-
this.maxSize = options.maxSize ?? 10
217+
this.maxSize = options.maxSize ?? DEFAULT_BATCH_MAX_SIZE
215218

216219
this.mapSubrequest = options.mapSubrequest ?? ((subRequest, { request: batchRequest }) => ({
217220
...subRequest,
@@ -223,7 +226,7 @@ export class BatchHandlerPlugin<T extends Context> implements StandardHandlerPlu
223226
* or `origin`, cannot be overridden by a subrequest, which is just request payload.
224227
*/
225228
...batchRequest.headers,
226-
'orpc-batch': undefined, // useful in case batch plugin is used multiple times
229+
[BATCH_HEADER]: undefined, // useful in case batch plugin is used multiple times
227230
},
228231
}))
229232

@@ -247,7 +250,7 @@ export class BatchHandlerPlugin<T extends Context> implements StandardHandlerPlu
247250

248251
init(options: StandardHandlerOptions<T>): StandardHandlerOptions<T> {
249252
const routingInterceptor: StandardHandlerRoutingInterceptor<T> = async (interceptorOptions) => {
250-
if (flattenStandardHeader(interceptorOptions.request.headers['orpc-batch']) === undefined) {
253+
if (flattenStandardHeader(interceptorOptions.request.headers[BATCH_HEADER]) === undefined) {
251254
return interceptorOptions.next()
252255
}
253256

‎packages/server/src/plugins/index.test.ts‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
it('exports plugins', async () => {
22
await expect(import('.')).resolves.toMatchObject({
33
BatchHandlerPlugin: expect.any(Function),
4+
BATCH_HEADER: expect.any(String),
5+
DEFAULT_BATCH_MAX_SIZE: expect.any(Number),
46
CORSHandlerPlugin: expect.any(Function),
57
RequestHeadersHandlerPlugin: expect.any(Function),
68
ResponseHeadersHandlerPlugin: expect.any(Function),

‎packages/server/src/plugins/index.ts‎

Lines changed: 1 addition & 0 deletions

0 commit comments

Comments
 (0)