React-Admin — Data Provider

Action Expected API request
Get list GET http://my.api.url/posts?sort=[‘title’,’ASC’]
Get one record GET http://my.api.url/posts/123
Get several records GET http://my.api.url/posts?filter={ids:[123,456,789]}
Update a record PUT http://my.api.url/posts/123
Create a record POST http://my.api.url/posts/123
Delete a record DELETE http://my.api.url/posts/123

Simple REST

npm install ra-data-simple-rest

import { fetchUtils, Admin, Resource } from 'react-admin';
import simpleRestProvider from 'ra-data-simple-rest';

const httpClient = (url, options = {}) => {
  options.user = {
    authenticated: true,
    token: 'SRTRDFVESGNJYTUKTYTHRG'
  }
  return fetchUtils.fetchJson(url, options);
}
const dataProvider = simpleRestProvider('http://path.to.my.api/', httpClient);

const App = () => (
  <Admin dataProvider={dataProvider}>
    <Resource name="posts" list={PostList} />
  </Admin>
);

Instead of writing your own Data Provider, you can enhance the capabilities of an existing data provider. To enhance a provider with the upload feature, compose addUploadFeature function with the data provider function:

import simpleRestProvider from 'ra-data-simple-rest';
import addUploadFeature from './addUploadFeature';

const dataProvider = simpleRestProvider('http://path.to.my.api/');
const uploadCapableDataProvider = addUploadFeature(dataProvider);

const App = () => (
  <Admin dataProvider={uploadCapableDataProvider}>
    <Resource name="posts" list={PostList} />
  </Admin>
);

Request Format

Data queries require a type (e.g. GET_ONE), a resource (e.g. ‘posts’) and a set of parameters.

dataProvider(GET_LIST, 'posts', {
  pagination: { page: 1, perPage: 5 },
  sort: { field: 'title', order: 'ASC' },
  filter: { author_id: 12 },
});

dataProvider(GET_ONE, 'posts', { id: 123 });

dataProvider(CREATE, 'posts', { data: { title: "hello, world" } });

dataProvider(UPDATE, 'posts', {
  id: 123,
  data: { title: "hello, world!" },
  previousData: { title: "previous title" }
});

dataProvider(UPDATE_MANY, 'posts', {
  ids: [123, 234],
  data: { views: 0 },
});

dataProvider(DELETE, 'posts', {
  id: 123,
  previousData: { title: "hello, world" }
});

dataProvider(DELETE_MANY, 'posts', { ids: [123, 234] });

dataProvider(GET_MANY, 'posts', { ids: [123, 124, 125] });

dataProvider(GET_MANY_REFERENCE, 'comments', {
  target: 'post_id',
  id: 123,
  sort: { field: 'created_at', order: 'DESC' }
});

Response Format

dataProvider(GET_LIST, 'posts', {
    pagination: { page: 1, perPage: 5 },
    sort: { field: 'title', order: 'ASC' },
    filter: { author_id: 12 },
})
.then(response => console.log(response));
// {
//     data: [
//         { id: 126, title: "allo?", author_id: 12 },
//         { id: 127, title: "bien le bonjour", author_id: 12 },
//         { id: 124, title: "good day sunshine", author_id: 12 },
//         { id: 123, title: "hello, world", author_id: 12 },
//         { id: 125, title: "howdy partner", author_id: 12 },
//     ],
//     total: 27
// }

dataProvider(GET_ONE, 'posts', { id: 123 })
.then(response => console.log(response));
// {
//     data: { id: 123, title: "hello, world" }
// }

dataProvider(CREATE, 'posts', { data: { title: "hello, world" } })
.then(response => console.log(response));
// {
//     data: { id: 450, title: "hello, world" }
// }

dataProvider(UPDATE, 'posts', {
    id: 123,
    data: { title: "hello, world!" },
    previousData: { title: "previous title" }
})
.then(response => console.log(response));
// {
//     data: { id: 123, title: "hello, world!" }
// }

dataProvider(UPDATE_MANY, 'posts', {
    ids: [123, 234],
    data: { views: 0 },
})
.then(response => console.log(response));
// {
//     data: [123, 234]
// }

dataProvider(DELETE, 'posts', {
    id: 123,
    previousData: { title: "hello, world!" }
})
.then(response => console.log(response));
// {
//     data: { id: 123, title: "hello, world" }
// }

dataProvider(DELETE_MANY, 'posts', { ids: [123, 234] })
.then(response => console.log(response));
// {
//     data: [123, 234]
// }

dataProvider(GET_MANY, 'posts', { ids: [123, 124, 125] })
.then(response => console.log(response));
// {
//     data: [
//         { id: 123, title: "hello, world" },
//         { id: 124, title: "good day sunshise" },
//         { id: 125, title: "howdy partner" },
//     ]
// }

dataProvider(GET_MANY_REFERENCE, 'comments', {
    target: 'post_id',
    id: 123,
    sort: { field: 'created_at', order: 'DESC' }
});
.then(response => console.log(response));
// {
//     data: [
//         { id: 667, title: "I agree", post_id: 123 },
//         { id: 895, title: "I don't agree", post_id: 123 },
//     ],
//     total: 2,
// }

Request Processing && Response Processing

Data Providers often use a switch statement, and finish by a call to fetch().

import { stringify } from 'query-string';
import {
  GET_LIST,
  GET_ONE,
  CREATE,
  UPDATE,
  DELETE,
  GET_MANY,
  GET_MANY_REFERENCE,
} from 'react-admin';

const apiUrl = 'http://path.to.my.api/';

export default (type, resource, params) => {
  let url = '';
  const options = {
    headers : new Headers({
      Accept: 'application/json',
    }),
  };
  switch (type) {
    case GET_LIST: {
      const { page, perPage } = params.pagination;
      const { field, order } = params.sort;
      const query = {
        sort: JSON.stringify([field, order]),
        range: JSON.stringify([
            (page - 1) * perPage,
            page * perPage - 1,
        ]),
        filter: JSON.stringify(params.filter),
      };
      url = `${apiUrl}/${resource}?${stringify(query)}`;
      break;
    }
    case GET_ONE:
      url = `${apiUrl}/${resource}/${params.id}`;
      break;
    case CREATE:
      url = `${apiUrl}/${resource}`;
      options.method = 'POST';
      options.body = JSON.stringify(params.data);
      break;
    case UPDATE:
      url = `${apiUrl}/${resource}/${params.id}`;
      options.method = 'PUT';
      options.body = JSON.stringify(params.data);
      break;
    case UPDATE_MANY:
      const query = {
        filter: JSON.stringify({ id: params.ids }),
      };
      url = `${apiUrl}/${resource}?${stringify(query)}`;
      options.method = 'PATCH';
      options.body = JSON.stringify(params.data);
      break;
    case DELETE:
      url = `${apiUrl}/${resource}/${params.id}`;
      options.method = 'DELETE';
      break;
    case DELETE_MANY:
      const query = {
        filter: JSON.stringify({ id: params.ids }),
      };
      url = `${apiUrl}/${resource}?${stringify(query)}`;
      options.method = 'DELETE';
      break;
    case GET_MANY: {
      const query = {
        filter: JSON.stringify({ id: params.ids }),
      };
      url = `${apiUrl}/${resource}?${stringify(query)}`;
      break;
    }
    case GET_MANY_REFERENCE: {
      const { page, perPage } = params.pagination;
      const { field, order } = params.sort;
      const query = {
        sort: JSON.stringify([field, order]),
        range: JSON.stringify([
          (page - 1) * perPage,
          page * perPage - 1,
        ]),
        filter: JSON.stringify({
          ...params.filter,
          [params.target]: params.id,
        }),
      };
      url = `${apiUrl}/${resource}?${stringify(query)}`;
      break;
    }
    default:
      throw new Error(`Unsupported Data Provider request type ${type}`);
  }

  return fetch(url, options)
    .then(res => {
        headers = res.headers;
        return res.json();
    })
    .then(json => {
      switch (type) {
        case GET_LIST:
        case GET_MANY_REFERENCE:
          if (!headers.has('content-range')) {
            throw new Error(
              'The Content-Range header is missing in the HTTP Response.'
            );
          }
          return {
            data: json,
            total: parseInt(
              headers
                .get('content-range')
                .split('/')
                .pop(),
                10
            ),
          };
        case CREATE:
          return { data: { ...params.data, id: json.id } };
        case DELETE_MANY:
          return { data: json || [] };
        default:
          return { data: json };
      }
    });
};
Category: React

Author: Yoga

Article
Tagcloud
DVA Java Express Architecture Azure CI/CD database ML AWS ETL nest sql AntV Next Deep Learning Flutter TypeScript Angular DevTools Microsoft egg Tableau SAP Token Regexp Unit test Nginx nodeJS sails wechat Jmeter HTML2Canvas Swift Jenkins JS event GTM Algorithm Echarts React-Admin Rest React hook Flux Redux ES6 Route Component Ref AJAX Form JSX Virtual Dom Javascript CSS design pattern