andyhite/redux-jsonapi

View on GitHub
README.md

Summary

Maintainability
Test Coverage
# Redux JSON:API

[![Travis CI](https://travis-ci.org/andyhite/redux-jsonapi.svg)](https://travis-ci.org/andyhite/redux-jsonapi)
[![Code Climate](https://codeclimate.com/github/andyhite/redux-jsonapi/badges/gpa.svg)](https://codeclimate.com/github/andyhite/redux-jsonapi)
[![Test Coverage](https://codeclimate.com/github/andyhite/redux-jsonapi/badges/coverage.svg)](https://codeclimate.com/github/andyhite/redux-jsonapi/coverage)

Redux JSON:API is a collection of actions and reducers for Redux that help ease the use of a [JSON:API](http://jsonapi.org/) compliant API.

JSON:API is amazing for data transport, but once a client has the data it can be a little frustrating to work with. Redux JSON:API takes the guess-work and bike-shedding about how to structure, store, and use your JSON:API data in a Redux application.

## Instalation

To install the stable version:

```
npm install --save redux-jsonapi
```

## Usage

### The Basics

The following will fetch a widget from `GET http://example.com/widgets/1` (including it's associated foobars) add it to the application state under `api` (indexed by it's ID), and output the new state to the console.

```js
import { createStore, combineReducers } from 'redux';
import { apiReducer, apiActions, createApiMiddleware } from 'redux-jsonapi';

const reducers = combineReducers({
  api: apiReducer
})

const apiMiddleware = createApiMiddleware('http://example.com');

const store = createStore(reducers, applyMiddleware(apiMiddleware);

store.subscribe(() => {
  console.log(store.getState().api);
});

store.dispatch(apiActions.read({ id: 1, _type: 'widgets' }, {
  params: {
    include: 'foobars'
  },
}));
```

Creating a widget via the API and adding it to the application state on success isn't much more complicated:

```js
store.dispatch(apiActions.write({ _type: 'widgets', name: 'Super Cool Widget' }));
```

Updating an existing record is nearly identical - simply make sure the resource has an ID property:

```js
store.dispatch(apiActions.write({ _type: 'widgets', id: 1, name: 'Super Cool Widget With A New Name' }));
```

Deleting a record is very similar:

```js
store.dispatch(apiActions.remove({ _type: 'widgets', id: 1 }));
```

#### Nested Resources

To access the entities on a nested URL, provide an array of parent resources to the action. The following will fetch the entities from `GET http://example.com/widgets/1/doodads`:

```js
store.dispatch(apiActions.read([{ _type: 'widgets', id: 1 }, { _type: 'doodads' }]));
```

Note: the order of resources in the array determines the order of nesting.

#### Custom Headers

You can extend the default headers by simply passing an object as 2nd parameter into `createApiMiddleware`.
```js
const apiMiddleware = createApiMiddleware('http://example.com', {
    Authorization:  'JWT Token'
});
```

With each `apiActions` method you can provide additional headers as well
```js
store.dispatch(apiActions.read({ id: 1, _type: 'widgets' }, {
  params: {
    include: 'foobars'
  },
  headers: {
    cookies: 'MyCookie'
  }
}));
```

### Using Data

Data received from the JSON:API store is normalized into categories by resource type, and indexed by the resource's ID. The structure looks something like the following:

```js
{
  api: {
    widgets: {
      1: {
        id: 1,
        type: 'widgets',
        attributes: {
          name: 'Super Cool Widget',
        },
        relationships: {
          doodad: {
            data: {
              id: 1,
              type: 'doodads',
            }
          }
        }
      },
      2: { ... },
    },
    doodads: {
      1: {
        id: 1,
        type: 'doodads',
        attributes: {
          name: 'Nifty Doodad',
        },
      },
      2: { ... },
    }
  }
}
```

Since often the data that we receive from the API contains associations to other resources, this isn't a very easy structure to work with out of the box. Redux JSON:API contains utilities that make serializing and deserializing resources between the JSON:API structure and a vanilla Javascript object structure much easier.

The following will fetch a widget from the API at /widgets/1, and upon being loaded into the Redux state it will log a *deserialized* instance of it to the console.

```js
import { apiActions, deserialize } from 'redux-jsonapi';

store.subscribe(() => {
  const { api } = store.getState();
  const widget = deserialize(api.widgets[1], api);
  console.log(widget);
});

store.dispatch(apiActions.read({ id: 1, _type: 'widgets' }));
```

The denormalized instance looks something like this:

```js
const widget = {
  _type: 'widgets',
  id: 1,
  name: 'Super Cool Widget',
  doodad: function() { // Calling this returns the associated doodad }
}
```

With this new denormalized resource, we can access the widget's name via `widget.name`, and it's associated doodad by calling `widget.doodad()`.

When it comes time to update the widget, simply change it's values and dispatch the `apiActions.write` action with the object.

```js
widget.name = 'New Name';
widget.doodad = () => newDoodad;
store.dispatch(apiActions.write(widget));
```

## Example

See the [example](https://github.com/andyhite/redux-jsonapi/tree/master/example) directory.

## License

MIT