From 56c6762c7de362148b7e723bd542060c6cf79119 Mon Sep 17 00:00:00 2001 From: fzaninotto Date: Thu, 2 Jan 2025 13:58:37 +0100 Subject: [PATCH] [Doc] Misc data provider doc changes --- docs/DataProviders.md | 468 ++++++++++++++++++++++-------------------- docs/List.md | 8 +- 2 files changed, 244 insertions(+), 232 deletions(-) diff --git a/docs/DataProviders.md b/docs/DataProviders.md index b01a6d0593..baabb37868 100644 --- a/docs/DataProviders.md +++ b/docs/DataProviders.md @@ -30,28 +30,26 @@ A Data Provider must implement the following methods: ```jsx const dataProvider = { - getList: (resource, params) => Promise, // get a list of records based on sort, filter, and pagination - getOne: (resource, params) => Promise, // get a single record by id - getMany: (resource, params) => Promise, // get a list of records based on an array of ids - getManyReference: (resource, params) => Promise, // get records referenced to another record, e.g., comments for a post - create: (resource, params) => Promise, // create a record - update: (resource, params) => Promise, // update a record - updateMany: (resource, params) => Promise, // update multiple records - delete: (resource, params) => Promise, // delete a record by id - deleteMany: (resource, params) => Promise, // delete multiple records + async getList(resource, { sort, filter, pagination }) => ({ data: Record[], total: number }), + async getOne(resource, { id }) => ({ data: Record }), + async getMany(resource, { ids }) => ({ data: Record[] }), + async getManyReference(resource, { target, id, sort, filter, pagination }) => ({ data: Record[], total: number }), + async create(resource, { data }) => ({ data: Record }), + async update(resource, { id, data }) => ({ data: Record }), + async updateMany(resource, { ids, data }) => ({ data: Identifier[] }), + async delete(resource, { id } ) => ({ data: Record }), + async deleteMany(resource, { ids }) => ({ data: Identifier[] }), } ``` **Tip**: A Data Provider can have [additional methods](#adding-custom-methods) beyond these 9. For example, you can add custom methods for non-REST API endpoints, tree structure manipulations, or real-time updates. -The Data Provider is a key part of react-admin's architecture. By standardizing the Data Provider interface, react-admin can offer powerful features, like reference handling, optimistic updates, and automated navigation. +The Data Provider is a key part of react-admin's architecture. By standardizing the Data Provider interface, react-admin can offer powerful features, like reference handling, optimistic updates, and autogenerated CRUD components. ## `` The first step to using a Data Provider is to pass it to [the `` component](./Admin.md) via the `dataProvider` prop. -You can either pick a data provider from the list of [supported API backends](./DataProviderList.md) or [write your own](./DataProviderWriting.md). - For example, let's use [the Simple REST data provider](https://github.com/marmelab/react-admin/tree/master/packages/ra-data-simple-rest). This provider is suitable for REST APIs using simple GET parameters for filters and sorting. First, install the `ra-data-simple-rest` package: @@ -60,7 +58,7 @@ First, install the `ra-data-simple-rest` package: yarn add ra-data-simple-rest ``` -Then, initialize the provider with the REST backend URL, and pass it to the `dataProvider` prop of the `` component: +Then, initialize the provider with the REST backend URL, and pass it as the ``: ```jsx // in src/App.js @@ -81,9 +79,7 @@ const App = () => ( export default App; ``` -That's all it takes to make all react-admin components work with your API. - -Here's how this Data Provider maps react-admin calls to API calls: +That's all it takes to make all react-admin components work with your API. They will call the data provider methods, which will in turn call the API. Here's how the Simple REST data provider maps react-admin calls to API calls: | Method name | API call | | ------------------ | --------------------------------------------------------------------------------------- | @@ -97,18 +93,182 @@ Here's how this Data Provider maps react-admin calls to API calls: | `delete` | `DELETE http://my.api.url/posts/123` | | `deleteMany` | Multiple calls to `DELETE http://my.api.url/posts/123` | -**Note**: The Simple REST client expects the API to include a `Content-Range` header in the response to `getList` calls, which indicates the total number of resources available. This helps react-admin build pagination controls. +For your own API, look for a compatible data provider in the list of [supported API backends](./DataProviderList.md) or [write your own](./DataProviderWriting.md). + +## Calling The Data Provider + +React-admin provides hooks for calling each of the data provider methods through react-query's `useQuery` and `useMutation` hooks. + +For instance, to call `dataProvider.getOne()`, use the `useGetOne` hook: + +```jsx +import { useGetOne } from 'react-admin'; +import { Loading, Error } from './MyComponents'; + +const UserProfile = ({ userId }) => { + const { data: user, isPending, error } = useGetOne('users', { id: userId }); + + if (isPending) return ; + if (error) return ; + if (!user) return null; + + return ( +
    +
  • Name: {user.name}
  • +
  • Email: {user.email}
  • +
+ ) +}; +``` + +The [Querying the API](./Actions.md) documentation lists all the hooks available for querying the API, as well as the options and return values for each of them. + +## Adding Custom Methods + +Your API backend may expose non-CRUD endpoints, e.g., for calling RPC endpoints. + +For instance, let's say your API exposes an endpoint to ban a user based on its `id`: ``` -Content-Range: posts 0-24/319 +POST /api/user/123/ban ``` -If your API is on a different domain from your JS code, you'll need to expose this header using the `Access-Control-Expose-Headers` [CORS](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS) header: +The react-admin way to expose these endpoints to the app components is to add a custom method in the `dataProvider`: +```jsx +import simpleRestDataProvider from 'ra-data-simple-rest'; + +const baseDataProvider = simpleRestDataProvider('http://path.to.my.api/'); + +export const dataProvider = { + ...baseDataProvider, + banUser: (userId) => { + return fetch(`/api/user/${userId}/ban`, { method: 'POST' }) + .then(response => response.json()); + }, +} ``` -Access-Control-Expose-Headers: Content-Range + +Then you can use react-query's `useMutation` hook to call the `dataProvider.banUser()` method: + +```jsx +import { useDataProvider } from 'react-admin'; +import { useMutation } from '@tanstack/react-query'; + +const BanUserButton = ({ userId }) => { + const dataProvider = useDataProvider(); + const { mutate, isPending } = useMutation({ + mutationFn: () => dataProvider.banUser(userId) + }); + return