diff --git a/src/index.d.ts b/src/index.d.ts index c1c6826..2aa4f84 100644 --- a/src/index.d.ts +++ b/src/index.d.ts @@ -147,6 +147,7 @@ export type BatchMiddlewareOpts = { batchTimeout?: number; maxBatchSize?: 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 2872e85..3ebc8ed 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 1367ef5..a72a4b2 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) @@ -21,6 +21,7 @@ export type BatchMiddlewareOpts = {| batchTimeout?: number, maxBatchSize?: 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 @@ -57,6 +58,7 @@ export default function batchMiddleware(options?: BatchMiddlewareOpts): Middlewa const allowMutations = opts.allowMutations || false; const batchUrl = opts.batchUrl || '/graphql/batch'; const maxBatchSize = opts.maxBatchSize || DEFAULT_BATCH_SIZE; + const allowOperation = opts.allowOperation || true; const singleton = {}; const fetchOpts = {}; @@ -79,6 +81,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); @@ -94,6 +100,7 @@ export default function batchMiddleware(options?: BatchMiddlewareOpts): Middlewa batchUrl, singleton, maxBatchSize, + allowOperation, fetchOpts, }); };