diff --git a/src/index.d.ts b/src/index.d.ts index 51d62fc..6b13013 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -148,6 +148,7 @@ export type BatchMiddlewareOpts = { maxBatchSize?: number; maxRequestsPerBatch?: number; allowMutations?: boolean; + allowOperation?: (operation: ConcreteBatch) => boolean; method?: 'POST' | 'GET'; headers?: Headers | Promise | ((req: RelayRequestBatch) => Headers | Promise); // Available request modes in fetch options. For details see https://fetch.spec.whatwg.org/#requests diff --git a/src/middlewares/__tests__/batch-test.js b/src/middlewares/__tests__/batch-test.js index a4a8800..92440ed 100644 --- a/src/middlewares/__tests__/batch-test.js +++ b/src/middlewares/__tests__/batch-test.js @@ -438,6 +438,60 @@ describe('middlewares/batch', () => { }); }); + describe('option `allowOperation`', () => { + beforeEach(() => { + fetchMock.restore(); + }); + + it('should not batch requests that return false', async () => { + fetchMock.mock({ + matcher: '/graphql', + response: { + status: 200, + body: { data: {} }, + }, + method: 'POST', + }); + fetchMock.mock({ + matcher: '/graphql/batch', + response: { + status: 200, + body: [{ data: {} }, { data: {} }, { data: {} }], + }, + method: 'POST', + }); + + const req1 = mockReq(1, { + query: 'abc', + }); + const req2 = mockReq(2); + const req3 = mockReq(3); + const req4 = mockReq(4, { + query: 'def', + }); + const req5 = mockReq(4, { + query: 'no', + }); + + const rnl = new RelayNetworkLayer([ + batchMiddleware({ + allowOperation: (op) => !['abc', 'def'].includes(op.text), + }), + ]); + await Promise.all([ + req1.execute(rnl), + req2.execute(rnl), + req3.execute(rnl), + req4.execute(rnl), + req5.execute(rnl), + ]); + const batchReqs = fetchMock.calls('/graphql/batch'); + const singleReqs = fetchMock.calls('/graphql'); + expect(batchReqs).toHaveLength(1); + expect(singleReqs).toHaveLength(2); + }); + }); + it('should pass fetch options', async () => { fetchMock.mock({ matcher: '/graphql/batch', diff --git a/src/middlewares/batch.js b/src/middlewares/batch.js index 19a66d3..6307b14 100644 --- a/src/middlewares/batch.js +++ b/src/middlewares/batch.js @@ -5,7 +5,7 @@ import { isFunction } from '../utils'; import RelayRequestBatch from '../RelayRequestBatch'; import RelayRequest from '../RelayRequest'; import type RelayResponse from '../RelayResponse'; -import type { Middleware, FetchOpts } from '../definition'; +import type { Middleware, FetchOpts, ConcreteBatch } from '../definition'; import RRNLError from '../RRNLError'; // Max out at roughly 100kb (express-graphql imposed max) @@ -22,6 +22,7 @@ export type BatchMiddlewareOpts = {| maxBatchSize?: number, maxRequestsPerBatch?: number, allowMutations?: boolean, + allowOperation?: (operation: ConcreteBatch) => boolean, method?: 'POST' | 'GET', headers?: Headers | Promise | ((req: RelayRequestBatch) => Headers | Promise), // Avaliable request modes in fetch options. For details see https://fetch.spec.whatwg.org/#requests @@ -59,6 +60,7 @@ export default function batchMiddleware(options?: BatchMiddlewareOpts): Middlewa const batchUrl = opts.batchUrl || '/graphql/batch'; const maxBatchSize = opts.maxBatchSize || DEFAULT_BATCH_SIZE; const maxRequestsPerBatch = opts.maxRequestsPerBatch || 0; // 0 is the same as no limit + const allowOperation = opts.allowOperation || true; const singleton = {}; const fetchOpts = {}; @@ -81,6 +83,10 @@ export default function batchMiddleware(options?: BatchMiddlewareOpts): Middlewa ); } + if (isFunction(opts.allowOperation) && !opts.allowOperation(req.operation)) { + return next(req); + } + // req with FormData can not be batched if (req.isFormData()) { return next(req); @@ -97,6 +103,7 @@ export default function batchMiddleware(options?: BatchMiddlewareOpts): Middlewa singleton, maxBatchSize, maxRequestsPerBatch, + allowOperation, fetchOpts, }); };