Skip to content

Commit d922161

Browse files
iamlacroixryanashcraft
authored andcommitted
Advanced middleware feature (amplitude#23)
* Add advanced middleware feature to inject custom network adapters This moves most of the logic from the query middleware to the new advanced middleware. The query middleware will call advanced using the default superagent adapter. * Add tests for the superagent adapter * Add advanced CommonJS entry point alias * Create separate default and advanced entries * Fix advanced invalid imports * Add test for PATCH in superagent adapter * Update README with docs about redux-query/advanced
1 parent 908a9b2 commit d922161

File tree

8 files changed

+452
-339
lines changed

8 files changed

+452
-339
lines changed

README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,53 @@ The result of the promise returned by `mutateAsync` will be the following object
256256

257257
Similarly to how mutations are triggered by dispatching `mutateAsync` actions, you can trigger requests by dispatching `requestAsync` actions with a request query config.
258258

259+
### Usage without superagent with `redux-query/advanced`
260+
261+
By default, `redux-query` makes XHR requests using the [superagent](https://github.com/visionmedia/superagent) library. If you'd rather use a different library for making requests, you can use the `redux-query`'s "advanced" mode by importing from `redux-query/advanced` instead of `redux-query`.
262+
263+
Note: The default [`queryMiddleware`](./src/middleware/query.js) exported from the main `redux-query` entry point is simply a [superagent adapter](./src/adapters/superagent.js) bound to `queryMiddlewareAdvanced`.
264+
265+
Example `queryMiddlewareAdvanced` usage:
266+
267+
```javascript
268+
import { applyMiddleware, createStore, combineReducers } from 'redux';
269+
import { entitiesReducer, queriesReducer, queryMiddlewareAdvanced } from 'redux-query/advanced';
270+
271+
// A function that takes a url, method, and other options. This function should return an object
272+
// with two required properties: execute and abort.
273+
import myNetworkAdapter from './network-adapter';
274+
275+
export const getQueries = (state) => state.queries;
276+
export const getEntities = (state) => state.entities;
277+
278+
const reducer = combineReducers({
279+
entities: entitiesReducer,
280+
queries: queriesReducer,
281+
});
282+
283+
const store = createStore(
284+
reducer,
285+
applyMiddleware(queryMiddlewareAdvanced(myNetworkAdapter)(getQueries, getEntities))
286+
);
287+
```
288+
289+
#### Network adapters
290+
291+
You must provide a function to `queryMiddlewareAdvanced` that adheres to the following `NetworkAdapter` interface:
292+
293+
```javascript
294+
type NetworkAdapter = (
295+
url: string,
296+
method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE',
297+
config?: { body?: string | Object, headers?: Object, credentials?: 'omit' | 'include' } = {},
298+
) => Adapter;
299+
300+
type NetworkRequest = {
301+
execute: (callback: (err: any, resStatus: number, resBody: ?Object, resText: string) => void) => void,
302+
abort: () => void,
303+
};
304+
```
305+
259306
## Example
260307

261308
A fork of the `redux` [Async](https://github.com/reactjs/redux/tree/master/examples/async) example is included. To run, first build the package:

advanced.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
module.exports = require('./dist/commonjs/advanced');

src/adapters/superagent.js

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
import superagent from 'superagent';
2+
import * as httpMethods from '../constants/http-methods';
3+
4+
export const createRequest = (url, method) => {
5+
switch (method) {
6+
case httpMethods.GET:
7+
return superagent.get(url);
8+
case httpMethods.POST:
9+
return superagent.post(url);
10+
case httpMethods.PUT:
11+
return superagent.put(url);
12+
case httpMethods.PATCH:
13+
return superagent.patch(url);
14+
case httpMethods.DELETE:
15+
return superagent.del(url);
16+
default:
17+
throw new Error(`Unsupported HTTP method: ${method}`);
18+
}
19+
};
20+
21+
const superagentNetworkAdapter = (url, method, { body, headers, credentials } = {}) => {
22+
const request = createRequest(url, method);
23+
24+
if (body) {
25+
request.send(body);
26+
}
27+
28+
if (headers) {
29+
request.set(headers);
30+
}
31+
32+
if (credentials === 'include') {
33+
request.withCredentials();
34+
}
35+
36+
const execute = (cb) => request.end((err, response) => {
37+
const resStatus = (response && response.status) || 0;
38+
const resBody = (response && response.body) || undefined;
39+
const resText = (response && response.text) || undefined;
40+
41+
cb(err, resStatus, resBody, resText);
42+
});
43+
44+
const abort = () => request.abort();
45+
46+
return { execute, abort };
47+
};
48+
49+
export default superagentNetworkAdapter;

src/advanced.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import * as actions from './actions';
2+
import * as actionTypes from './constants/action-types';
3+
import * as httpMethods from './constants/http-methods';
4+
import * as querySelectors from './selectors/query';
5+
6+
export { default as connectRequest } from './components/connect-request';
7+
export { getQueryKey, reconcileQueryKey } from './lib/query-key';
8+
export { default as queriesReducer } from './reducers/queries';
9+
export { default as entitiesReducer } from './reducers/entities';
10+
export { default as queryMiddlewareAdvanced } from './middleware/query-advanced';
11+
export { cancelQuery, mutateAsync, requestAsync, removeEntities, removeEntity } from './actions';
12+
export { actions, actionTypes, httpMethods, querySelectors };

0 commit comments

Comments
 (0)