Data Fetching & Data Providers
In a shadmin app, you don’t write API calls using fetch or axios. Instead, you communicate with your API through an object called the dataProvider.
This documentation will explain the following concepts:
- What is a Data Provider?
- How to set up a Data Provider
- Supported Data Provider backends
- How to write a Data Provider
- How to query the API using hooks
The dataProvider
Shadmin streamlines data fetching for administrative interfaces through its Data Provider object, which unifies interactions across diverse APIs such as REST and GraphQL. This abstraction allows developers to focus on UI development rather than intricate API calls. It employs specialized hooks, like useGetList and useGetOne, and integrates TanStack Query to manage data efficiently, offering features such as caching and optimistic updates.
The framework also simplifies working with relational APIs and incorporates real-time capabilities for collaborative applications. Authentication is handled by an authProvider, which manages user logins and tokens, subsequently utilized by the dataProvider for secure API requests.
To learn more about the Data Provider, refer to the Data Fetching documentation.
Setup
The first step to using a Data Provider is to pass it to the <Admin> component via the dataProvider prop.
For example, let’s use the Simple REST data provider. This provider is suitable for REST APIs using simple GET parameters for filters and sorting.
First, install the ra-data-simple-rest package:
npm install ra-data-simple-rest
Then, initialize the provider with the REST backend URL, and pass it as the <Admin dataProvider>:
// in src/App.js
import { Admin } from "@/components/admin";
import { Resource } from "ra-core";
import simpleRestProvider from "ra-data-simple-rest";
import { PostList } from "./posts";
const dataProvider = simpleRestProvider("http://path.to.my.api/");
const App = () => (
<Admin dataProvider={dataProvider}>
<Resource name="posts" list={PostList} />
</Admin>
);
export default App;That’s all it takes to make all shadmin 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 shadmin calls to API calls:
| Method name | API call |
|---|---|
getList | GET http://my.api.url/posts?sort=["title","ASC"]&range=[0, 24]&filter={"title":"bar"} |
getOne | GET http://my.api.url/posts/123 |
getMany | GET http://my.api.url/posts?filter={"ids":[123,456,789]} |
getManyReference | GET http://my.api.url/posts?filter={"author_id":345} |
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 |
deleteMany | Multiple calls to DELETE http://my.api.url/posts/123 |
For your own API, look for a compatible data provider in the list of supported API backends or write your own.
For more details about the data provider setup, refer to the Data Provider Setup documentation.
Supported Data Provider Backends
Thanks to the Data Provider architecture, shadmin supports a lot of API backends. Check the list below for open-source packages developed and maintained by the core team and developers from the community.
If you can't find a Data Provider for your backend below, no worries! Writing a Data Provider takes a couple of hours, and won't prevent you from using shadmin.
- Appwrite: marmelab/ra-appwrite
- AWS Amplify: MrHertal/react-admin-amplify
- Blitz-js: theapexlab/ra-data-blitz
- RConfigurable Identity Property REST Client: zachrybaker/ra-data-rest-client
- coreBOS: React-Admin coreBOS Integration
- Directus: marmelab/ra-directus
- Django Rest Framework: bmihelac/ra-data-django-rest-framework
- Eicrud: danyalutsevich/ra-data-eicrud
- Eve: smeng9/ra-data-eve
- Express & Mongoose: NathanAdhitya/express-mongoose-ra-json-server
- Express & Sequelize: express-sequelize-crud
- FakeRest: marmelab/ra-data-fakerest
- Feathersjs: josx/ra-data-feathers
- Firebase Firestore: benwinding/react-admin-firebase.
- Firebase Realtime Database: aymendhaya/ra-data-firebase-client.
- GeoServer: sergioedo/ra-data-geoserver
- Genezio: bogdanripa/react-admin-genezio
- Google Sheets: marmelab/ra-data-google-sheets
- GraphQL (generic): marmelab/ra-data-graphql (uses Apollo)
- GraphQL (simple): marmelab/ra-data-graphql-simple.
- HHAL: b-social/ra-data-hal
- Hasura: hasura/ra-data-hasura
- Hydra / JSON-LD: api-platform/admin/hydra
- IndexedDB (via LocalForage): marmelab/ra-data-local-forage
- IndexedDB: tykoth/ra-data-dexie
- JSON API: henvo/ra-jsonapi-client
- JJSON HAL: ra-data-json-hal
- JSON server: marmelab/ra-data-json-server
- LinuxForHealth FHIR: tum-mri-aiim/ra-data-fhir
- LocalStorage: marmelab/ra-data-local-storage
- LocalStorage (via LocalForage): marmelab/ra-data-local-forage
- Loopback3: darthwesker/react-admin-loopback
- Loopback4: elmaistrenko/react-admin-lb4
- Loopback4 CRUD: loopback4/ra-data-lb4
- Mixer: ckoliber/ra-data-mixer
- Moleculer Microservices: RancaguaInnova/moleculer-data-provider
- NestJS CRUD: rayman1104/ra-data-nestjsx-crud
- Nestjs-query (GraphQL): mrnkr/ra-data-nestjs-query
- OData: Groopit/ra-data-odata-server
- OpenCRUD: weakky/ra-data-opencrud
- Parse: almahdi/ra-data-parse
- PostGraphile: bowlingx/ra-postgraphile
- PostgREST: raphiniert-com/ra-data-postgrest
- Prisma v1: weakky/ra-data-prisma
- Prisma v2 (GraphQL): panter/ra-data-prisma
- Prisma v2 (REST): codeledge/ra-data-simple-prisma
- ProcessMaker3: ckoliber/ra-data-processmaker3
- REST-HAPI: ra-data-rest-hapi
- Sails.js: mpampin/ra-data-json-sails
- SQLite: marmelab/ra-sqlite-dataprovider
- REST: marmelab/ra-data-simple-rest
- Soul/SQLite: DeepBlueCLtd/RA-Soul
- Spring Boot: vishpat/ra-data-springboot-rest
- Strapi v3/v4: nazirov91/ra-strapi-rest
- Strapi v4: garridorafa/ra-strapi-v4-rest
- Strapi v5: marmelab/ra-strapi
- Supabase: marmelab/ra-supabase-core
- Supabase (GraphQL): @groovestack/ra-data-graphql-supabase
- SurrealDB: djedi23/ra-surrealdb
- TreeQL / PHP-CRUD-API: nkappler/ra-data-treeql
- WooCommerce REST API: zackha/ra-data-woocommerce
That's a long list!
If you don't know where to start, use any of the following:
- marmelab/ra-data-fakerest: Simulates an API based on a JSON object. It doesn't even require a server.
- marmelab/ra-data-json-server: Similar to the previous one, but requires an API powered by JSONServer.
- marmelab/ra-data-simple-rest: A basic REST adapter that reflects the structure of many APIs
- marmelab/ra-data-local-storage: Persists user editions in local storage. This allows local-first apps, and can be useful in tests.
- marmelab/ra-data-local-forage: Uses a local, offline database based on IndexedDB. Falls back to WebSQL or localStorage.
Tip: Since dataProviders all present the same interface, you can use one dataProvider during early prototyping / development phases, then switch to the dataProvider that fits your production infrastructure.
If you've written a Data Provider for another backend, and open-sourced it, please help complete this list with your package.
Writing a 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.
A data provider must implement the following methods:
const dataProvider = {
// get a list of records based on sort, filter, and pagination
getList: (resource, params) => Promise,
// get a single record by id
getOne: (resource, params) => Promise,
// get a list of records based on an array of ids
getMany: (resource, params) => Promise,
// get the records referenced to another record, e.g. comments for a post
getManyReference: (resource, params) => Promise,
// create a record
create: (resource, params) => Promise,
// update a record based on a patch
update: (resource, params) => Promise,
// update a list of records based on an array of ids and a common patch
updateMany: (resource, params) => Promise,
// delete a record by id
delete: (resource, params) => Promise,
// delete a list of records based on an array of ids
deleteMany: (resource, params) => Promise,
};To call the data provider, shadmin combines 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.
To learn more about writing a Data Provider, refer to the Data Provider Writing documentation.
Querying The API
Shadmin provides special hooks to emit read and write queries to the dataProvider, which in turn sends requests to your API. Under the hood, it uses React Query to call the dataProvider and cache the results.
Shadmin provides one query hook for each of the Data Provider read methods. They are useful shortcuts that make your code more readable and more robust. The query hooks execute on mount. They return an object with the following properties: { data, isPending, error }. Query hooks are:
useGetListcallsdataProvider.getList()useGetOnecallsdataProvider.getOne()useGetManycallsdataProvider.getMany()useGetManyReferencecallsdataProvider.getManyReference()
Their input signature is the same as the related dataProvider method, i.e. they expect the resource name and the query parameters:
const { isPending, error, data } = useGetOne(resource, { id });
// calls dataProvider.getOne(resource, { id })For instance, here is how to fetch one User record on mount using the useGetOne hook:
import { useGetOne } from "ra-core";
import { Loading, Error } from "./MyComponents";
const UserProfile = ({ userId }) => {
const { isPending, error, data: user } = useGetOne("users", { id: userId });
if (isPending) return <Loading />;
if (error) return <Error />;
if (!user) return null;
return (
<ul>
<li>Name: {user.name}</li>
<li>Email: {user.email}</li>
</ul>
);
};Shadmin also provides one mutation hook for each of the Data Provider write methods. These hooks execute the query when you call a callback. They return an array with the following items: [mutate, { data, isPending, error }]. mutate is a callback that you can call to execute the mutation.
Mutation hooks are:
useCreatecallsdataProvider.create()useUpdatecallsdataProvider.update()useUpdateManycallsdataProvider.updateMany()useDeletecallsdataProvider.delete()useDeleteManycallsdataProvider.deleteMany()
Their input signature is the same as the related dataProvider method, e.g.:
const [update, { isPending, error, data }] = useUpdate(resource, {
id,
data,
previousData,
});
// calls dataProvider.update(resource, { id, data, previousData })For instance, here is a button that updates a comment record when clicked, using the useUpdate hook:
import { useUpdate, useRecordContext } from "ra-core";
const ApproveButton = () => {
const record = useRecordContext();
const [approve, { isPending }] = useUpdate("comments", {
id: record.id,
data: { isApproved: true },
previousData: record,
});
return (
<button onClick={() => approve()} disabled={isPending}>
Approve
</button>
);
};For more information and examples, refer to the Data Fetching documentation.