--- layout: default title: "Querying the API" --- # Querying the API Admin interfaces often have to query the API beyond CRUD requests. For instance, a user profile page may need to get the User object based on a user id. Or, users may want to an "Approve" a comment by pressing a button, and this action should update the `is_approved` property and save the updated record in one click. React-admin provides special hooks to emit read and write queries to the [`dataProvider`](./DataProviders.md), which in turn sends requests to your API. ## `useDataProvider` Hook React-admin stores the `dataProvider` object in a React context, so it's available from anywhere in your application code. The `useDataProvider` hook exposes the Data Provider to let you call it directly. For instance, here is how to query the Data Provider for the current user profile: ```jsx import React, { useState, useEffect } from 'react'; import { useDataProvider, Loading, Error } from 'react-admin'; const UserProfile = ({ userId }) => { const dataProvider = useDataProvider(); const [user, setUser] = useState(); const [loading, setLoading] = useState(true); const [error, setError] = useState(); useEffect(() => { dataProvider.getOne('users', { id: userId }) .then(({ data }) => { setUser(data); setLoading(false); }) .catch(error => { setError(error); setLoading(false); }) }, []); if (loading) return ; if (error) return ; if (!user) return null; return ( ) }; ``` **Tip**: The `dataProvider` returned by the hook is actually a *wrapper* around your Data Provider. This wrapper dispatches Redux actions on load, success and failure, which keeps track of the loading state. ## `useQuery` Hook The `useQuery` hook calls the Data Provider on mount, and returns an object that updates as the response arrives. It reduces the boilerplate code for calling the Data Provider. For instance, the previous code snippet can be rewritten with `useQuery` as follows: ```jsx import React from 'react'; import { useQuery, Loading, Error } from 'react-admin'; const UserProfile = ({ userId }) => { const { data, loading, error } = useQuery({ type: 'getOne', resource: 'users', payload: { id: userId } }); if (loading) return ; if (error) return ; if (!data) return null; return (
  • Name: {data.name}
  • Email: {data.email}
) }; ``` `useQuery` expects a Query argument with the following keys: - `type`: The method to call on the Data Provider, e.g. `getList` - `resource`: The Resource name, e.g. "posts" - `params`: The query parameters. Depends on the query type. The return value of `useQuery` is an object representing the query state, using the following keys: - `data`: `undefined` until the response arrives, then contains the `data` key in the `dataProvider` response - `total`: `null` until the response arrives, then contains the `total` key in the `dataProvider` response (only for `getList` and `getManyReference` types) - `error`: `null` unless the `dataProvider` threw an error, in which case it contains that error. - `loading`: A boolean updating according to the request state - `loaded`: A boolean updating according to the request state This object updates according to the request state: - start: `{ loading: true, loaded: false }` - success: `{ data: [data from response], total: [total from response], loading: false, loaded: true }` - error: `{ error: [error from response], loading: false, loaded: true }` As a reminder, here are the read query types handled by Data Providers: Type | Usage | Params format | Response format ------------------ | ------------------------------------------------|--------------------------- | --------------- `getList` | Search for resources | `{ pagination: { page: {int} , perPage: {int} }, sort: { field: {string}, order: {string} }, filter: {Object} }` | `{ data: {Record[]}, total: {int} }` `getOne` | Read a single resource, by id | `{ id: {mixed} }` | `{ data: {Record} }` `getMany` | Read a list of resource, by ids | `{ ids: {mixed[]} }` | `{ data: {Record[]} }` `getManyReference` | Read a list of resources related to another one | `{ target: {string}, id: {mixed}, pagination: { page: {int} , perPage: {int} }, sort: { field: {string}, order: {string} }, filter: {Object} }` | `{ data: {Record[]} }` ## `useQueryWithStore` Hook React-admin exposes a more powerful version of `useQuery`. `useQueryWithStore` persist the response from the `dataProvider` in the internal react-admin Redux store, so that result remains available if the hook is called again in the future. You can use this hook to show the cached result immediately on mount, while the updated result is fetched from the API. This is called optimistic rendering. ```diff import React from 'react'; -import { useQuery, Loading, Error } from 'react-admin'; +import { useQueryWithStore, Loading, Error } from 'react-admin'; const UserProfile = ({ record }) => { - const { loaded, error, data } = useQuery({ + const { loaded, error, data } = useQueryWithStore({ type: 'getOne', resource: 'users', payload: { id: record.id } }); if (!loaded) { return ; } if (error) { return ; } return
User {data.username}
; }; ``` In practice, react-admin uses `useQueryWithStore` instead of `useQuery` everywhere, and you should probably do the same in your components. It really improves the User Experience, with only one little drawback: if the data changed on the backend side between two calls for the same query, the user may briefly see outdated data before the screen updates with the up-to-date data. ## `useMutation` Hook `useQuery` emits the request to the `dataProvider` as soon as the component mounts. To emit the request based on a user action, use the `useMutation` hook instead. This hook takes the same arguments as `useQuery`, but returns a callback that emits the request when executed. Here is an implementation of an "Approve" button: ```jsx import React from 'react'; import { useMutation, Button } from 'react-admin'; const ApproveButton = ({ record }) => { const [approve, { loading }] = useMutation({ type: 'update', resource: 'comments', payload: { id: record.id, data: { isApproved: true } } }); return