diff --git a/docs/DataProviders.md b/docs/DataProviders.md index 47cb21e71d5..d7ee73e073c 100644 --- a/docs/DataProviders.md +++ b/docs/DataProviders.md @@ -1,6 +1,6 @@ --- layout: default -title: "Data Providers" +title: 'Data Providers' --- # Data Providers @@ -8,14 +8,12 @@ title: "Data Providers" Whenever react-admin needs to communicate with the API, it calls methods on the Data Provider object. ```js -dataProvider - .getOne('posts', { id: 123 }) - .then(response => { - console.log(response.data); // { id: 123, title: "hello, world" } - }); +dataProvider.getOne('posts', { id: 123 }).then(response => { + console.log(response.data); // { id: 123, title: "hello, world" } +}); ``` -It's the Data Provider's job to turn these method calls into HTTP requests, and transform the HTTP responses to the data format expected by react-admin. In technical terms, a Data Provider is an *adapter* for an API. +It's the Data Provider's job to turn these method calls into HTTP requests, and transform the HTTP responses to the data format expected by react-admin. In technical terms, a Data Provider is an _adapter_ for an API. And to inject a Data Provider in a react-admin application, pass it as the `dataProvider` prop of the `` component, as follows: @@ -23,11 +21,7 @@ And to inject a Data Provider in a react-admin application, pass it as the `data import { Admin, Resource } from 'react-admin'; import dataProvider from '../myDataProvider'; -const App = () => ( - - // ... - -) +const App = () => // ...; ``` Thanks to this adapter injection system, react-admin can communicate with any API, whether it uses REST, GraphQL, RPC, or even SOAP, regardless of the dialect it uses. The Data Provider is also the ideal place to add custom HTTP headers, authentication, etc. @@ -38,16 +32,16 @@ A Data Provider must have the following methods: ```jsx const dataProvider = { - getList: (resource, params) => Promise, - getOne: (resource, params) => Promise, - getMany: (resource, params) => Promise, + getList: (resource, params) => Promise, + getOne: (resource, params) => Promise, + getMany: (resource, params) => Promise, getManyReference: (resource, params) => Promise, - create: (resource, params) => Promise, - update: (resource, params) => Promise, + create: (resource, params) => Promise, + update: (resource, params) => Promise, updateMany: (resource, params) => Promise, - delete: (resource, params) => Promise, + delete: (resource, params) => Promise, deleteMany: (resource, params) => Promise, -} +}; ``` You can find an example Data Provider implementation at the end of this chapter. @@ -60,60 +54,61 @@ You can find an example Data Provider implementation at the end of this chapter. The react-admin project includes 5 Data Providers: -* Simple REST: [marmelab/ra-data-simple-rest](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-simple-rest) ([read more below](#usage)). It serves mostly as an example. Incidentally, it is compatible with the [FakeRest](https://github.com/marmelab/FakeRest) API. -* **[JSON server](https://github.com/typicode/json-server)**: [marmelab/ra-data-json-server](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-json-server). Great for prototyping an admin over a yet-to-be-developed REST API. -* [Simple GraphQL](https://graphql.org/): [marmelab/ra-data-graphql-simple](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql-simple). A GraphQL provider built with Apollo and tailored to target a simple GraphQL implementation. -* Local JSON: [marmelab/ra-data-fakerest](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-fakerest). Based on a local object, it doesn't even use HTTP. Use it for testing purposes. -* Local Storage: [marmelab/ra-data-localstorage](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-localstorage). User editions are persisted across refreshes and between sessions. This allows local-first apps, and can be useful in tests. +- Simple REST: [marmelab/ra-data-simple-rest](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-simple-rest) ([read more below](#usage)). It serves mostly as an example. Incidentally, it is compatible with the [FakeRest](https://github.com/marmelab/FakeRest) API. +- **[JSON server](https://github.com/typicode/json-server)**: [marmelab/ra-data-json-server](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-json-server). Great for prototyping an admin over a yet-to-be-developed REST API. +- [Simple GraphQL](https://graphql.org/): [marmelab/ra-data-graphql-simple](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql-simple). A GraphQL provider built with Apollo and tailored to target a simple GraphQL implementation. +- Local JSON: [marmelab/ra-data-fakerest](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-fakerest). Based on a local object, it doesn't even use HTTP. Use it for testing purposes. +- Local Storage: [marmelab/ra-data-localstorage](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-localstorage). User editions are persisted across refreshes and between sessions. This allows local-first apps, and can be useful in tests. +- Supabase: [marmelab/ra-supabase](https://github.com/marmelab/ra-supabase). A provider built for [Supabase](https://supabase.io/). Developers from the react-admin community have open-sourced Data Providers for many more backends: -* **[AWS Amplify](https://docs.amplify.aws)**: [MrHertal/react-admin-amplify](https://github.com/MrHertal/react-admin-amplify) -* **[Configurable Identity Property REST Client](https://github.com/zachrybaker/ra-data-rest-client)**: [zachrybaker/ra-data-rest-client](https://github.com/zachrybaker/ra-data-rest-client) -* **[coreBOS](https://corebos.com/)**: [React-Admin coreBOS Integration](https://github.com/coreBOS/reactadminportal) -* **[Django Rest Framework](https://www.django-rest-framework.org/)**: [synaptic-cl/ra-data-drf](https://github.com/synaptic-cl/ra-data-drf) -* **[Eve](https://docs.python-eve.org/en/stable/)**: [smeng9/ra-data-eve](https://github.com/smeng9/ra-data-eve) -* **[Express & Sequelize](https://github.com/lalalilo/express-sequelize-crud)**: [express-sequelize-crud](https://github.com/lalalilo/express-sequelize-crud) -* **[Feathersjs](https://www.feathersjs.com/)**: [josx/ra-data-feathers](https://github.com/josx/ra-data-feathers) -* **[Firebase Firestore](https://firebase.google.com/docs/firestore)**: [benwinding/react-admin-firebase](https://github.com/benwinding/react-admin-firebase). -* **[Firebase Realtime Database](https://firebase.google.com/docs/database)**: [aymendhaya/ra-data-firebase-client](https://github.com/aymendhaya/ra-data-firebase-client). -* **[Google Sheets](https://www.google.com/sheets/about/)**: [marmelab/ra-data-google-sheets](https://github.com/marmelab/ra-data-google-sheets) -* **[GraphQL](https://graphql.org/)**: [marmelab/ra-data-graphql](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql) (uses [Apollo](https://www.apollodata.com/)) -* **[HAL](http://stateless.co/hal_specification.html)**: [b-social/ra-data-hal](https://github.com/b-social/ra-data-hal) -* **[Hasura](https://github.com/hasura/graphql-engine)**: [hasura/ra-data-hasura](https://github.com/hasura/ra-data-hasura), auto generates valid GraphQL queries based on the properties exposed by the Hasura API. -* **[Hydra](https://www.hydra-cg.com/) / [JSON-LD](https://json-ld.org/)**: [api-platform/admin/hydra](https://github.com/api-platform/admin/blob/master/src/hydra/dataProvider.js) -* **[IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)**: [tykoth/ra-data-dexie](https://github.com/tykoth/ra-data-dexie) -* **[JSON API](https://jsonapi.org/)**: [henvo/ra-jsonapi-client](https://github.com/henvo/ra-jsonapi-client) -* **[JSON HAL](https://tools.ietf.org/html/draft-kelly-json-hal-08)**: [ra-data-json-hal](https://www.npmjs.com/package/ra-data-json-hal) -* **[JSON server](https://github.com/typicode/json-server)**: [marmelab/ra-data-json-server](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-json-server). -* **[LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)**: [marmelab/ra-data-localstorage](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-localstorage) -* **[Loopback3](https://loopback.io/lb3)**: [darthwesker/react-admin-loopback](https://github.com/darthwesker/react-admin-loopback) -* **[Loopback4](https://loopback.io/)**: [elmaistrenko/react-admin-lb4](https://github.com/elmaistrenko/react-admin-lb4) -* **[Loopback4 CRUD](https://github.com/loopback4/loopback-component-crud)**: [loopback4/ra-data-lb4](https://github.com/loopback4/ra-data-lb4) -* **[Mixer](https://github.com/ckoliber/ra-data-mixer)**: [ckoliber/ra-data-mixer](https://github.com/ckoliber/ra-data-mixer) -* **[Moleculer Microservices](https://github.com/RancaguaInnova/moleculer-data-provider)**: [RancaguaInnova/moleculer-data-provider](https://github.com/RancaguaInnova/moleculer-data-provider) -* **[NestJS CRUD](https://github.com/nestjsx/crud)**: [rayman1104/ra-data-nestjsx-crud](https://github.com/rayman1104/ra-data-nestjsx-crud) -* **[OData](https://www.odata.org/)**: [Groopit/ra-data-odata-server](https://github.com/Groopit/ra-data-odata-server) -* **[OpenCRUD](https://www.opencrud.org/)**: [weakky/ra-data-opencrud](https://github.com/Weakky/ra-data-opencrud) -* **[Parse](https://parseplatform.org/)**: [almahdi/ra-data-parse](https://github.com/almahdi/ra-data-parse) -* **[PostGraphile](https://www.graphile.org/postgraphile/)**: [bowlingx/ra-postgraphile](https://github.com/BowlingX/ra-postgraphile) -* **[PostgREST](https://postgrest.org/)**: [raphiniert-com/ra-data-postgrest](https://github.com/raphiniert-com/ra-data-postgrest) -* **[Prisma](https://github.com/weakky/ra-data-prisma)**: [weakky/ra-data-prisma](https://github.com/weakky/ra-data-prisma) -* **[Prisma Version 2](https://www.prisma.io/)**: [panter/ra-data-prisma](https://github.com/panter/ra-data-prisma) -* **[ProcessMaker3](https://www.processmaker.com/)**: [ckoliber/ra-data-processmaker3](https://github.com/ckoliber/ra-data-processmaker3) -* **[REST-HAPI](https://github.com/JKHeadley/rest-hapi)**: [ra-data-rest-hapi](https://github.com/mkg20001/ra-data-rest-hapi) -* **[Sails.js](https://sailsjs.com/)**: [mpampin/ra-data-json-sails](https://github.com/mpampin/ra-data-json-sails) -* **[Spring Boot](https://spring.io/projects/spring-boot)**: [vishpat/ra-data-springboot-rest](https://github.com/vishpat/ra-data-springboot-rest) -* **[Strapi](https://strapi.io/)**: [nazirov91/ra-strapi-rest](https://github.com/nazirov91/ra-strapi-rest) +- **[AWS Amplify](https://docs.amplify.aws)**: [MrHertal/react-admin-amplify](https://github.com/MrHertal/react-admin-amplify) +- **[Configurable Identity Property REST Client](https://github.com/zachrybaker/ra-data-rest-client)**: [zachrybaker/ra-data-rest-client](https://github.com/zachrybaker/ra-data-rest-client) +- **[coreBOS](https://corebos.com/)**: [React-Admin coreBOS Integration](https://github.com/coreBOS/reactadminportal) +- **[Django Rest Framework](https://www.django-rest-framework.org/)**: [synaptic-cl/ra-data-drf](https://github.com/synaptic-cl/ra-data-drf) +- **[Eve](https://docs.python-eve.org/en/stable/)**: [smeng9/ra-data-eve](https://github.com/smeng9/ra-data-eve) +- **[Express & Sequelize](https://github.com/lalalilo/express-sequelize-crud)**: [express-sequelize-crud](https://github.com/lalalilo/express-sequelize-crud) +- **[Feathersjs](https://www.feathersjs.com/)**: [josx/ra-data-feathers](https://github.com/josx/ra-data-feathers) +- **[Firebase Firestore](https://firebase.google.com/docs/firestore)**: [benwinding/react-admin-firebase](https://github.com/benwinding/react-admin-firebase). +- **[Firebase Realtime Database](https://firebase.google.com/docs/database)**: [aymendhaya/ra-data-firebase-client](https://github.com/aymendhaya/ra-data-firebase-client). +- **[Google Sheets](https://www.google.com/sheets/about/)**: [marmelab/ra-data-google-sheets](https://github.com/marmelab/ra-data-google-sheets) +- **[GraphQL](https://graphql.org/)**: [marmelab/ra-data-graphql](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-graphql) (uses [Apollo](https://www.apollodata.com/)) +- **[HAL](http://stateless.co/hal_specification.html)**: [b-social/ra-data-hal](https://github.com/b-social/ra-data-hal) +- **[Hasura](https://github.com/hasura/graphql-engine)**: [hasura/ra-data-hasura](https://github.com/hasura/ra-data-hasura), auto generates valid GraphQL queries based on the properties exposed by the Hasura API. +- **[Hydra](https://www.hydra-cg.com/) / [JSON-LD](https://json-ld.org/)**: [api-platform/admin/hydra](https://github.com/api-platform/admin/blob/master/src/hydra/dataProvider.js) +- **[IndexedDB](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)**: [tykoth/ra-data-dexie](https://github.com/tykoth/ra-data-dexie) +- **[JSON API](https://jsonapi.org/)**: [henvo/ra-jsonapi-client](https://github.com/henvo/ra-jsonapi-client) +- **[JSON HAL](https://tools.ietf.org/html/draft-kelly-json-hal-08)**: [ra-data-json-hal](https://www.npmjs.com/package/ra-data-json-hal) +- **[JSON server](https://github.com/typicode/json-server)**: [marmelab/ra-data-json-server](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-json-server). +- **[LocalStorage](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)**: [marmelab/ra-data-localstorage](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-localstorage) +- **[Loopback3](https://loopback.io/lb3)**: [darthwesker/react-admin-loopback](https://github.com/darthwesker/react-admin-loopback) +- **[Loopback4](https://loopback.io/)**: [elmaistrenko/react-admin-lb4](https://github.com/elmaistrenko/react-admin-lb4) +- **[Loopback4 CRUD](https://github.com/loopback4/loopback-component-crud)**: [loopback4/ra-data-lb4](https://github.com/loopback4/ra-data-lb4) +- **[Mixer](https://github.com/ckoliber/ra-data-mixer)**: [ckoliber/ra-data-mixer](https://github.com/ckoliber/ra-data-mixer) +- **[Moleculer Microservices](https://github.com/RancaguaInnova/moleculer-data-provider)**: [RancaguaInnova/moleculer-data-provider](https://github.com/RancaguaInnova/moleculer-data-provider) +- **[NestJS CRUD](https://github.com/nestjsx/crud)**: [rayman1104/ra-data-nestjsx-crud](https://github.com/rayman1104/ra-data-nestjsx-crud) +- **[OData](https://www.odata.org/)**: [Groopit/ra-data-odata-server](https://github.com/Groopit/ra-data-odata-server) +- **[OpenCRUD](https://www.opencrud.org/)**: [weakky/ra-data-opencrud](https://github.com/Weakky/ra-data-opencrud) +- **[Parse](https://parseplatform.org/)**: [almahdi/ra-data-parse](https://github.com/almahdi/ra-data-parse) +- **[PostGraphile](https://www.graphile.org/postgraphile/)**: [bowlingx/ra-postgraphile](https://github.com/BowlingX/ra-postgraphile) +- **[PostgREST](https://postgrest.org/)**: [raphiniert-com/ra-data-postgrest](https://github.com/raphiniert-com/ra-data-postgrest) +- **[Prisma](https://github.com/weakky/ra-data-prisma)**: [weakky/ra-data-prisma](https://github.com/weakky/ra-data-prisma) +- **[Prisma Version 2](https://www.prisma.io/)**: [panter/ra-data-prisma](https://github.com/panter/ra-data-prisma) +- **[ProcessMaker3](https://www.processmaker.com/)**: [ckoliber/ra-data-processmaker3](https://github.com/ckoliber/ra-data-processmaker3) +- **[REST-HAPI](https://github.com/JKHeadley/rest-hapi)**: [ra-data-rest-hapi](https://github.com/mkg20001/ra-data-rest-hapi) +- **[Sails.js](https://sailsjs.com/)**: [mpampin/ra-data-json-sails](https://github.com/mpampin/ra-data-json-sails) +- **[Spring Boot](https://spring.io/projects/spring-boot)**: [vishpat/ra-data-springboot-rest](https://github.com/vishpat/ra-data-springboot-rest) +- **[Strapi](https://strapi.io/)**: [nazirov91/ra-strapi-rest](https://github.com/nazirov91/ra-strapi-rest) If you've written a Data Provider for another backend, and open-sourced it, please help complete this list with your package. **Tip**: In version 1, react-admin was called [admin-on-rest](https://github.com/marmelab/admin-on-rest) (AOR), and developers shared Data Providers for even more backends. Due to breaking changes in v2, these providers are no longer working. Fortunately, Data Providers aren't complex pieces of code, and using legacy Data Provider with a recent react-admin version requires minimal changes. If you are a maintainer of one of these projects, we would warmly welcome an upgrade. -* **[DynamoDb](https://github.com/abiglobalhealth/aor-dynamodb-client)**: [abiglobalhealth/aor-dynamodb-client](https://github.com/abiglobalhealth/aor-dynamodb-client) -* **[Epilogue](https://github.com/dchester/epilogue)**: [dunghuynh/aor-epilogue-client](https://github.com/dunghuynh/aor-epilogue-client) -* **[Parse Server](https://github.com/ParsePlatform/parse-server)**: [leperone/aor-parseserver-client](https://github.com/leperone/aor-parseserver-client) -* **[Xmysql](https://github.com/o1lab/xmysql)**: [soaserele/aor-xmysql](https://github.com/soaserele/aor-xmysql) +- **[DynamoDb](https://github.com/abiglobalhealth/aor-dynamodb-client)**: [abiglobalhealth/aor-dynamodb-client](https://github.com/abiglobalhealth/aor-dynamodb-client) +- **[Epilogue](https://github.com/dchester/epilogue)**: [dunghuynh/aor-epilogue-client](https://github.com/dunghuynh/aor-epilogue-client) +- **[Parse Server](https://github.com/ParsePlatform/parse-server)**: [leperone/aor-parseserver-client](https://github.com/leperone/aor-parseserver-client) +- **[Xmysql](https://github.com/o1lab/xmysql)**: [soaserele/aor-xmysql](https://github.com/soaserele/aor-xmysql) ## Usage @@ -129,7 +124,7 @@ Then, initialize the provider with the REST backend URL, and pass the result to ```jsx // in src/App.js -import * as React from "react"; +import * as React from 'react'; import { Admin, Resource } from 'react-admin'; import simpleRestProvider from 'ra-data-simple-rest'; @@ -152,7 +147,7 @@ Here is how this Data Provider maps react-admin calls to API calls: | `getOne` | `GET http://my.api.url/posts/123` | | `getMany` | `GET http://my.api.url/posts?filter={"id":[123,456,789]}` | | `getManyReference` | `GET http://my.api.url/posts?filter={"author_id":345}` | -| `create` | `POST http://my.api.url/posts` | +| `create` | `POST http://my.api.url/posts` | | `update` | `PUT http://my.api.url/posts/123` | | `updateMany` | Multiple calls to `PUT http://my.api.url/posts/123` | | `delete` | `DELETE http://my.api.url/posts/123` | @@ -174,7 +169,7 @@ Access-Control-Expose-Headers: Content-Range The `simpleRestProvider` function accepts an HTTP client function as second argument. By default, it uses react-admin's `fetchUtils.fetchJson()` function as HTTP client. It's similar to HTML5 `fetch()`, except it handles JSON decoding and HTTP error codes automatically. -That means that if you need to add custom headers to your requests, you can just *wrap* the `fetchJson()` call inside your own function: +That means that if you need to add custom headers to your requests, you can just _wrap_ the `fetchJson()` call inside your own function: ```jsx import { fetchUtils, Admin, Resource } from 'react-admin'; @@ -187,7 +182,7 @@ const fetchJson = (url, options = {}) => { // add your own headers here options.headers.set('X-Custom-Header', 'foobar'); return fetchUtils.fetchJson(url, options); -} +}; const dataProvider = simpleRestProvider('http://path.to.my.api/', fetchJson); const App = () => ( @@ -205,7 +200,7 @@ Now all the requests to the REST API will contain the `X-Custom-Header: foobar` const fetchJson = (url, options = {}) => { options.user = { authenticated: true, - token: 'SRTRDFVESGNJYTUKTYTHRG' + token: 'SRTRDFVESGNJYTUKTYTHRG', }; return fetchUtils.fetchJson(url, options); }; @@ -216,7 +211,7 @@ Now all the requests to the REST API will contain the `Authorization: SRTRDFVESG ## Extending a Data Provider (Example of File Upload) -As Data Providers are just objects, you can extend them with custom logic for a given method, or a given resource. +As Data Providers are just objects, you can extend them with custom logic for a given method, or a given resource. For instance, the following Data Provider extends the `ra-data-simple-rest` provider, and adds image upload support for the `update('posts')` call (react-admin offers an `` component that allows image upload). @@ -236,7 +231,7 @@ const myDataProvider = { * For posts update only, convert uploaded image in base 64 and attach it to * the `picture` sent property, with `src` and `title` attributes. */ - + // Freshly dropped pictures are File objects and must be converted to base64 strings const newPictures = params.data.pictures.filter( p => p.rawFile instanceof File @@ -288,11 +283,11 @@ Using this technique, you can also combine two Data Providers for two backends i ## Writing Your Own Data Provider -APIs are so diverse that quite often, none of the available Data Providers suit you API. In such cases, you'll have to write your own Data Provider. Don't worry, it usually takes only a couple of hours. +APIs are so diverse that quite often, none of the available Data Providers suit you API. In such cases, you'll have to write your own Data Provider. Don't worry, it usually takes only a couple of hours. The methods of a Data Provider receive a request, and return a promise for a response. Both the request and the response format are standardized. -**Caution**: A Data Provider should return the same shape in `getList` and `getOne` for a given resource. This is because react-admin uses "optimistic rendering", and renders the Edit and Show view *before* calling `dataProvider.getOne()` by reusing the response from `dataProvider.getList()` if the user has displayed the List view before. If your API has different shapes for a query for a unique record and for a query for a list of records, your Data Provider should make these records consistent in shape before returning them to react-admin. +**Caution**: A Data Provider should return the same shape in `getList` and `getOne` for a given resource. This is because react-admin uses "optimistic rendering", and renders the Edit and Show view _before_ calling `dataProvider.getOne()` by reusing the response from `dataProvider.getList()` if the user has displayed the List view before. If your API has different shapes for a query for a unique record and for a query for a list of records, your Data Provider should make these records consistent in shape before returning them to react-admin. For instance, the following Data Provider returns more details in `getOne` than in `getList`: @@ -301,25 +296,25 @@ const { data } = await dataProvider.getList('posts', { pagination: { page: 1, perPage: 5 }, sort: { field: 'title', order: 'ASC' }, filter: { author_id: 12 }, -}) +}); // [ // { id: 123, title: "hello, world", author_id: 12 }, // { id: 125, title: "howdy partner", author_id: 12 }, // ], -const { data } = dataProvider.getOne('posts', { id: 123 }) +const { data } = dataProvider.getOne('posts', { id: 123 }); // { // data: { id: 123, title: "hello, world", author_id: 12, body: 'Lorem Ipsum Sic Dolor Amet' } // } ``` -This will cause the Edit view to blink on load. If you have this problem, modify your Data Provider to return the same shape for all methods. +This will cause the Edit view to blink on load. If you have this problem, modify your Data Provider to return the same shape for all methods. ## Request Format -Data queries require a *method* (e.g. `getOne`), a *resource* (e.g. 'posts') and a set of *parameters*. +Data queries require a _method_ (e.g. `getOne`), a _resource_ (e.g. 'posts') and a set of _parameters_. -**Tip**: In comparison, HTTP requests require a *verb* (e.g. 'GET'), an *url* (e.g. 'http://myapi.com/posts'), a list of *headers* (like `Content-Type`) and a *body*. +**Tip**: In comparison, HTTP requests require a _verb_ (e.g. 'GET'), an _url_ (e.g. 'http://myapi.com/posts'), a list of _headers_ (like `Content-Type`) and a _body_. Standard methods are: @@ -348,21 +343,21 @@ dataProvider.getMany('posts', { ids: [123, 124, 125] }); dataProvider.getManyReference('comments', { target: 'post_id', id: 123, - sort: { field: 'created_at', order: 'DESC' } + sort: { field: 'created_at', order: 'DESC' }, }); dataProvider.update('posts', { id: 123, - data: { title: "hello, world!" }, - previousData: { title: "previous title" } + data: { title: 'hello, world!' }, + previousData: { title: 'previous title' }, }); dataProvider.updateMany('posts', { ids: [123, 234], data: { views: 0 }, }); -dataProvider.create('posts', { data: { title: "hello, world" } }); +dataProvider.create('posts', { data: { title: 'hello, world' } }); dataProvider.delete('posts', { id: 123, - previousData: { title: "hello, world" } + previousData: { title: 'hello, world' }, }); dataProvider.deleteMany('posts', { ids: [123, 234] }); ``` @@ -390,12 +385,13 @@ A `{Record}` is an object literal with at least an `id` property, e.g. `{ id: 12 Building up on the previous example, here are example responses matching the format expected by react-admin: ```js -dataProvider.getList('posts', { - pagination: { page: 1, perPage: 5 }, - sort: { field: 'title', order: 'ASC' }, - filter: { author_id: 12 }, -}) -.then(response => console.log(response)); +dataProvider + .getList('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 }, @@ -407,14 +403,16 @@ dataProvider.getList('posts', { // total: 27 // } -dataProvider.getOne('posts', { id: 123 }) -.then(response => console.log(response)); +dataProvider + .getOne('posts', { id: 123 }) + .then(response => console.log(response)); // { // data: { id: 123, title: "hello, world" } // } -dataProvider.getMany('posts', { ids: [123, 124, 125] }) -.then(response => console.log(response)); +dataProvider + .getMany('posts', { ids: [123, 124, 125] }) + .then(response => console.log(response)); // { // data: [ // { id: 123, title: "hello, world" }, @@ -423,12 +421,13 @@ dataProvider.getMany('posts', { ids: [123, 124, 125] }) // ] // } -dataProvider.getManyReference('comments', { - target: 'post_id', - id: 123, - sort: { field: 'created_at', order: 'DESC' } -}) -.then(response => console.log(response)); +dataProvider + .getManyReference('comments', { + target: 'post_id', + id: 123, + sort: { field: 'created_at', order: 'DESC' }, + }) + .then(response => console.log(response)); // { // data: [ @@ -438,42 +437,47 @@ dataProvider.getManyReference('comments', { // total: 2, // } -dataProvider.create('posts', { data: { title: "hello, world" } }) -.then(response => console.log(response)); +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)); +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.updateMany('posts', { - ids: [123, 234], - data: { views: 0 }, -}) -.then(response => console.log(response)); +dataProvider + .updateMany('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)); +dataProvider + .delete('posts', { + id: 123, + previousData: { title: 'hello, world!' }, + }) + .then(response => console.log(response)); // { // data: { id: 123, title: "hello, world" } // } -dataProvider.deleteMany('posts', { ids: [123, 234] }) -.then(response => console.log(response)); +dataProvider + .deleteMany('posts', { ids: [123, 234] }) + .then(response => console.log(response)); // { // data: [123, 234] // } @@ -532,7 +536,6 @@ export default { Let's say that you want to map the react-admin requests to a REST backend exposing the following API: - ### getList ``` @@ -707,7 +710,7 @@ export default { updateMany: (resource, params) => { const query = { - filter: JSON.stringify({ id: params.ids}), + filter: JSON.stringify({ id: params.ids }), }; return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, { method: 'PUT', @@ -730,7 +733,7 @@ export default { deleteMany: (resource, params) => { const query = { - filter: JSON.stringify({ id: params.ids}), + filter: JSON.stringify({ id: params.ids }), }; return httpClient(`${apiUrl}/${resource}?${stringify(query)}`, { method: 'DELETE', @@ -744,19 +747,19 @@ export default { React-admin stores the Data Provider passed to `` in a React context, so you can access it from anywhere in your code. To facilitate usage, react-admin provides many data provider hooks: -* `useDataProvider` -* `useQuery` -* `useQueryWithStore` -* `useMutation` -* `useGetList` -* `useGetOne` -* `useGetMany` -* `useGetManyReference` -* `useCreate` -* `useUpdate` -* `useUpdateMany` -* `useDelete` -* `useDeleteMany` +- `useDataProvider` +- `useQuery` +- `useQueryWithStore` +- `useMutation` +- `useGetList` +- `useGetOne` +- `useGetMany` +- `useGetManyReference` +- `useCreate` +- `useUpdate` +- `useUpdateMany` +- `useDelete` +- `useDeleteMany` Here is a glimpse of the `useGetOne` hook usage: @@ -765,8 +768,12 @@ import { useGetOne } from 'react-admin'; const UserProfile = ({ record }) => { const { data, loading, error } = useGetOne('users', record.id); - if (loading) { return ; } - if (error) { return

ERROR

; } + if (loading) { + return ; + } + if (error) { + return

ERROR

; + } return
User {data.username}
; }; ``` @@ -775,9 +782,9 @@ You will find complete usage documentation for the data provider hooks in the [Q ## Real-Time Updates And Locks -Teams where several people work in parallel on a common task need to allow live updates, real-time notifications, and prevent data loss when two editors work on the same resource concurrently. +Teams where several people work in parallel on a common task need to allow live updates, real-time notifications, and prevent data loss when two editors work on the same resource concurrently. -[`ra-realtime`](https://marmelab.com/ra-enterprise/modules/ra-realtime) (an [Enterprise Edition ](https://marmelab.com/ra-enterprise) module) provides hooks and UI components to lock records, and update views when the underlying data changes. It's based on the Publish / Subscribe (PubSub) pattern, and requires a backend supporting this pattern (like GraphQL, Mercure). +[`ra-realtime`](https://marmelab.com/ra-enterprise/modules/ra-realtime) (an [Enterprise Edition ](https://marmelab.com/ra-enterprise) module) provides hooks and UI components to lock records, and update views when the underlying data changes. It's based on the Publish / Subscribe (PubSub) pattern, and requires a backend supporting this pattern (like GraphQL, Mercure). For instance, here is how to enable live updates on a List view: @@ -805,4 +812,3 @@ const PostList = props => ( ``` Check [the `ra-realtime` documentation](https://marmelab.com/ra-enterprise/modules/ra-realtime) for more details. -