diff --git a/README.md b/README.md index 34388e9..9af3adc 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,21 @@ Middlewares - `prefix` - prefix message (default: `[RELAY-NETWORK] GRAPHQL SERVER ERROR:`) - **deferMiddleware** - _experimental_ Right now `deferMiddleware()` just set `defer` as supported option for Relay. So this middleware allow to community play with `defer()` in cases, which was [described by @wincent](https://github.com/facebook/relay/issues/288#issuecomment-199510058). +Advanced options (2nd argument after middlewares) +=========== + +RelayNetworkLayer may accept additional options: + +```js +const middlewares = []; // array of middlewares +const options = {}; // optional advanced options +const network = new RelayNetworkLayer(middlewares, options); +``` + +Available options: + +- **noThrow** - EXPERIMENTAL (May be deprecated in the future) set true to not throw when an error response is given by the server, and to instead handle errors in your app code. + ### Example of injecting NetworkLayer with middlewares on the **client side**. ```js import Relay from 'react-relay'; diff --git a/src/__tests__/fetchWithMiddleware.test.js b/src/__tests__/fetchWithMiddleware.test.js index 022fc53..00b0fd5 100644 --- a/src/__tests__/fetchWithMiddleware.test.js +++ b/src/__tests__/fetchWithMiddleware.test.js @@ -35,7 +35,7 @@ describe('fetchWithMiddleware', () => { it('should make a successfull request without middlewares', async () => { fetchMock.post('/graphql', { id: 1, data: { user: 123 } }); - const data = await fetchWithMiddleware(createMockReq(1), []); + const { data } = await fetchWithMiddleware(createMockReq(1), [], {}); expect(data).toEqual({ user: 123 }); }); @@ -53,10 +53,14 @@ describe('fetchWithMiddleware', () => { fetchMock.post('/graphql', { id: 1, data: { num: 1 } }); - const data = await fetchWithMiddleware(createMockReq(1), [ - numPlus5, - numMultiply10, // should be first, when changing response - ]); + const { data } = await fetchWithMiddleware( + createMockReq(1), + [ + numPlus5, + numMultiply10, // should be first, when changing response + ], + {} + ); expect(data).toEqual({ num: 15 }); }); }); diff --git a/src/definition.js b/src/definition.js index c796471..8889cd1 100644 --- a/src/definition.js +++ b/src/definition.js @@ -59,7 +59,7 @@ export type RRNLResponseObject = { statusText: string, headers: { [name: string]: string }, url: string, - payload: ?GraphQLResponse, + payload?: GraphQLResponse, }; export type RelayClassicRequest = { @@ -71,3 +71,7 @@ export type RelayClassicRequest = { getVariables: () => Object, getDebugName: () => string, }; + +export type RRNLOptions = { + noThrow?: boolean, +}; diff --git a/src/fetchWithMiddleware.js b/src/fetchWithMiddleware.js index 67bf41d..7427fcf 100644 --- a/src/fetchWithMiddleware.js +++ b/src/fetchWithMiddleware.js @@ -7,6 +7,7 @@ import type { RRNLRequestObject, RRNLResponseObject, MiddlewareNextFn, + RRNLOptions, } from './definition'; function runFetch(req: RRNLRequestObject): Promise { @@ -40,16 +41,27 @@ function runFetch(req: RRNLRequestObject): Promise { export default function fetchWithMiddleware( req: RRNLRequestObject, - middlewares: Middleware[] + middlewares: Middleware[], + options: RRNLOptions ): Promise { const wrappedFetch: MiddlewareNextFn = compose(...middlewares)(runFetch); return wrappedFetch(req).then(res => { const { payload } = res; - if (!payload || payload.hasOwnProperty('errors') || !payload.hasOwnProperty('data')) { + const { noThrow = false } = options; + const hasErrors = + !payload || payload.hasOwnProperty('errors') || !payload.hasOwnProperty('data'); + + /** Only throw the Error if noThrow === false */ + if (!noThrow && hasErrors) { throw createRequestError(req, res); } - return payload.data; + + /** Return payload.data as well as the errors (if they exist) */ + return { + data: (payload && payload.data) || null, + errors: hasErrors ? createRequestError(req, res) : null, + }; }); } diff --git a/src/middleware/__tests__/auth.test.js b/src/middleware/__tests__/auth.test.js index fa7f591..82b7cd7 100644 --- a/src/middleware/__tests__/auth.test.js +++ b/src/middleware/__tests__/auth.test.js @@ -41,7 +41,6 @@ describe('Middleware / auth', () => { it('should work with mutation', async () => { const req1 = mockReq(); await rnl.sendMutation(req1); - expect(req1.payload).toEqual({ response: 'PAYLOAD' }); const reqs = fetchMock.calls('/graphql'); expect(reqs).toHaveLength(1); diff --git a/src/relayMutation.js b/src/relayMutation.js index 39ec5ef..5577126 100644 --- a/src/relayMutation.js +++ b/src/relayMutation.js @@ -26,7 +26,7 @@ export default function mutation( } return fetchWithMiddleware(req) - .then(data => relayRequest.resolve({ response: data })) + .then(({ data }) => relayRequest.resolve({ response: data })) .catch(err => { relayRequest.reject(err); throw err; diff --git a/src/relayNetworkLayer.js b/src/relayNetworkLayer.js index 8753376..1ebdef6 100644 --- a/src/relayNetworkLayer.js +++ b/src/relayNetworkLayer.js @@ -3,9 +3,7 @@ import queries from './relayQueries'; import mutation from './relayMutation'; import fetchWithMiddleware from './fetchWithMiddleware'; -import type { Middleware, RelayClassicRequest } from './definition'; - -export type RRNLOptions = {}; +import type { Middleware, RelayClassicRequest, RRNLOptions } from './definition'; export default class RelayNetworkLayer { _options: RRNLOptions; @@ -16,7 +14,7 @@ export default class RelayNetworkLayer { sendMutation: Function; constructor(middlewares: Middleware[] | Middleware, options?: RRNLOptions) { - this._options = options || {}; + this._options = typeof options === 'object' ? options : {}; this._middlewares = Array.isArray(middlewares) ? middlewares : [middlewares]; this._supportedOptions = []; @@ -40,10 +38,10 @@ export default class RelayNetworkLayer { } sendQueries(requests: RelayClassicRequest[]): Promise { - return queries(requests, req => fetchWithMiddleware(req, this._middlewares)); + return queries(requests, req => fetchWithMiddleware(req, this._middlewares, this._options)); } sendMutation(request: RelayClassicRequest): Promise { - return mutation(request, req => fetchWithMiddleware(req, this._middlewares)); + return mutation(request, req => fetchWithMiddleware(req, this._middlewares, this._options)); } } diff --git a/src/relayQueries.js b/src/relayQueries.js index 4235d77..40763f3 100644 --- a/src/relayQueries.js +++ b/src/relayQueries.js @@ -26,7 +26,7 @@ export default function queries( }; return fetchWithMiddleware(req) - .then(data => relayRequest.resolve({ response: data })) + .then(({ data }) => relayRequest.resolve({ response: data })) .catch(err => { relayRequest.reject(err); throw err;