Project

General

Profile

Download (5.45 KB) Statistics
| Branch: | Tag: | Revision:
[[api-middleware-intro]]

= API Middleware Usage
:toc: right
:toclevels: 5

# API Middleware

Instead of each component handling API calls in the same way we have the API Middleware that will handle it.

## How to use API in a Component using useAPI hook

The API middleware is abstracted by the `useAPI` custom hook.

```js
import { useAPI } from '../common/hooks/API';
import KEY_FOR_API from './consts';
import { successCallback, errorCallback } from './helper';

const MyComponent = () => {
const options = {
handleSuccess: successCallback,
handleError: error => (error.response.status === 401 ? logoutUser() : null),
successToast: response => 'This text will be shown as a toast after a success call',
errorToast: response => 'This text will be shown as a toast when error occurs',
};
const {
response: { results },
status, // The current status of the API call
key, // Generated key for storing in redux's store
setAPIOptions, // Function to update the options and make a new api call
} = useAPI('get', '/api/hosts', options);
return (
<ul>
<button onClick={() => setAPIOptions({ ...options, params: { search: 'os=fedora' } })}>
Fedora
</button>
<button onClick={() => setAPIOptions({ ...options, params: { search: 'os=debian' } })}>
Debian
</button>
{results.map(item => (
<li key={item.id}>{item.title}</li>
))}
</ul>
);
};
```

## How to use the API middleware

The api middleware is a redux middleware that handles API calls in the application.
It is recommended to use the `useAPI` hook instead of using the middleware directly.


```js
/** MyComponent.js*/
import React from 'react';
import { STATUS } from '../../constants';

const MyComponent = ({ status, error, items }) => {
if (status === STATUS.PENDING) {
return <div>Loading...</div>;
}

if (status === STATUS.ERROR) {
return <div>Error: {error.message}</div>;
}

return (
<ul>
{items.map(item => (
<li key={item.id}>
{item.title} {item.action}
</li>
))}
</ul>
);
};
```

```js
/** index.js*/
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { useSelector, useDispatch } from 'react-redux';
import {
selectItems,
selectStatus,
selectError,
} from './MyComponentsSelectors.js';
import { getData } from './MyComponentActions';
import MyComponent from './MyComponent';

const ConnectedMyComponent = ({ path }) => {
const items = useSelector(selectItems);
const status = useSelector(selectStatus);
const error = useSelector(selectError);

const dispatch = useDispatch();

useEffect(() => {
dispatch(getData(path));
}, [path]);

return <MyComponent items={items} status={status} error={error} />;
};

ConnectedMyComponent.propTypes = {
path: PropTypes.string.isRequired,
};

export default ConnectedMyComponent;
```

### Access the API store

We provided you the `selectAPIByKey` in '/APISelectors.js' which will return the key substate,
there are also `selectAPIStatus`, `selectAPIPayload`, `selectAPIResponse`, `selectAPIError` and `selectAPIErrorMessage`.
```js
/** MyComponentSelectors.js*/

import { MY_SPECIAL_KEY } from './MyComponentConstants';
import {
selectAPIStatus,
selectAPIError,
selectAPIResponse,
} from '../../redux/API/APISelectors';

// use the same key that you've used in the API action.
export const selectItems = state =>
selectAPIResponse(state, MY_SPECIAL_KEY).items || [];

export const selectStatus = state => selectAPIStatus(state, MY_SPECIAL_KEY);

export const selectError = state => selectAPIError(state, MY_SPECIAL_KEY);
```

Then there will be called 2 actions: **MY_SPECIAL_KEY_REQUEST** and **MY_SPECIAL_KEY_SUCCESS/ MY_SPECIAL_KEY_FAILURE**:
**MY_SPECIAL_KEY_REQUEST** will have the payload only
**MY_SPECIAL_KEY_SUCCESS** will have the payload and the return data from the API call.
**MY_SPECIAL_KEY_FAILURE** will have the payload and the return error from the API call.

In the **payload** field you should send any headers and params for the GET request, and any other data you want for the action.

The actions types can be changed with the optional **actionTypes** parameter:

```js

/** MyComponentActions.js*/

export const getData = url => ({
type: API_OPERATIONS.GET,
payload: {
key: MY_SPECIAL_KEY, // you will need to re-use this key in order to access the right API reducer later.
url,
payload: {
page: 2,
per_page: 10,
},
actionTypes: {
REQUEST: 'CUSTOM_REQUEST',
}
},
});
```

The example API returns a JSON object like this:

```json
{
"items": [
{ "id": 319, "title": "setting", "action": "update" },
{ "id": 150, "title": "bookmark", "action": "create" }
]
}
```
Once the action is triggered, the API middleware will manage the request
and update the store with the request status:

the store on API pending:

```js
{
...
API: {
MY_SPECIAL_KEY: { // The key that was provided in the API action.
response: null,
status: "PENDING",
payload: {},
}
}
}
```

the store on API success:

```js
{
...
API: {
MY_SPECIAL_KEY: {
response: {
items: [
{id: 319, title: "setting", action: "update"},
{id: 150, title: "bookmark", action: "create"}
],
},
status: "RESOLVED",
payload: {},
}
}
}
```

the store on API failure:

```js
{
...
API: {
MY_SPECIAL_KEY: {
response: "Network Error",
status: "ERROR",
paylod: {},
}
}
}
```
(4-4/21)