@ra-data-prisma/dataprovider v6.7.0-beta.1
@ra-data-prisma/dataprovider
Data provider for react admin
Usage
yarn add @ra-data-prisma/dataprovider
Make sure your backend API is compatible by using the other package in this repo backend. Alternativly, you can connect to a backend that was created using typegraphql-prisma. See Chapter typegraphql-prisma
Add the dataprovider to your react-admin app:
import React, { Component } from "react"
import { Admin, Resource, Datagrid, TextField, Login } from "react-admin"
import { useDataProvider } from "@ra-data-prisma/dataprovider"
import { UserList, UserEdit, UserCreate } from "./resources/user"
import useAuthProvider from "./useAuthProvider"
const AdminApp = () => {
const dataProvider = useDataProvider({
clientOptions: { uri: "/graphql" }
aliasPrefix: "admin" // 👈 set this, if you use a aliasPrefix on your backend as well (recommended)
filters: {} // custom filters
queryDialect: "nexus-prisma" // customize query dialect, defaults to nexus-prisma
})
const authProvider = useAuthProvider()
if (!dataProvider) {
return <div>Loading</div>
}
return (
<Admin
dataProvider={dataProvider}
authProvider={authProvider}
>
<Resource
name="User"
list={UserList}
edit={UserEdit}
create={UserCreate}
/>
</Admin>
)
}
export default AdminAppFeatures
aliasPrefix
Set aliasPrefix if you have set it on the backend as well (see backend ).
Search & Filtering
this dataprovider supports all filtering and searching and adds some convenience to it:
- intelligent handling of strings and ints: you don't have to specify
equalsorcontains, we do that for you. The filter can just be{firstname: "albert"}and we create the correct graphql query for that - extended react-admin input fields: you can directly use comparison operators on
NumberInputs,TextInputsandDateInputs. For example, implementing "Created since" filter (DateInputon a fieldcreatedAt) would become<DateInput label="Created since" source="createdAt_gt" />- available comparisons (default comparison is the one which would be used if omitted):
- ints, floats and datetimes -
gt,gte,lt,lte,equals(default =equals) - strings -
gt,gte,lt,lte,equals,contains,startsWith,endsWith(default =contains)
- ints, floats and datetimes -
- available comparisons (default comparison is the one which would be used if omitted):
- case insensitive: If your Prisma version supports it (>= 2.8.0), we automatically query strings as case insensitive. If your Prisma version doesn't support it, we emulate it with query for multiple variations of the search term: as-is, fully lowercase, first letter uppercase. This does work in many cases (searching for terms and names), but fails in some.
- q query.
qis a convention in react-admin for general search. We implement this client side. A query onqwill search all string fields and int fields on a resource. It additionaly splits multiple search terms and does an AND search - if you need more sophisticated search, you can use normal nexus-prisma graphql queries. You can even mix it with
qand the intelligent short notation
Custom filters
if you have a complex query, you can define custom filters:
const dataProvider = useDataProvider({
clientOptions: { uri: "/graphql" }
filters: {
onlyMillenials: (value?: boolean) =>
value === true
? {
AND: [
{
yearOfBirth: {
gte: 1981,
},
},
{
yearOfBirth: {
lte: 1996,
},
},
],
}
: undefined,
},
})Then you can use that in a react-admin filter:
const UserFilter = (props) => (
<Filter {...props}>
<TextInput label="Search" source="q" alwaysOn />
<BooleanInput
label="Show only millenials"
source="onlyMillenials"
alwaysOn
/>
</Filter>
);Notice if you want to omit the filter, return null or undefined.
Relations
If you have relations, you can use ReferenceArrayField/Input or Referenceinput/Field. Make sure that the reference Model is also compatible (by calling addCrudResolvers("MyReferenceModel") from @ra-data-prisma/backend on your backend).
Sorting by relations
<List />s can be sorted by relations. Enable it in the backend
some examples:
show a list of cities with the country
export const CityList = (props) => (
<List {...props}>
<Datagrid>
<TextField source="id" />
<TextField source="name" />
<ReferenceField label="Country" source="country" reference="Country">
<TextField source="name" />
</ReferenceField>
<EditButton />
</Datagrid>
</List>
);show all user roles in the user list
export const UserList = (props) => (
<List {...props}>
<Datagrid>
<TextField source="id" />
<TextField source="username" />
<ReferenceArrayField
alwaysOn
label="Roles"
source="roles"
reference="UserRole"
>
<SingleFieldList>
<ChipField source="name" />
</SingleFieldList>
</ReferenceArrayField>
<EditButton />
</Datagrid>
</List>
);edit the roles for a user
export const UserEdit = (props) => (
<Edit title={<UserTitle />} {...props} undoable={false}>
<SimpleForm variant="outlined">
<TextInput source="userName" />
<ReferenceArrayInput
label="Roles"
source="roles"
reference="UserRole"
allowEmpty
fullWidth
>
<SelectArrayInput optionText="id" />
</ReferenceArrayInput>
</SimpleForm>
</Edit>
);Customize fetching & virtual Resources
react-admin has no mechanism to tell the dataprovider which fields are requested for any resources,
we therefore load all fields for a resource. If a field points to an existing Resource, we only fetch the id of that resource.
But sometimes you need to customize this behaviour, e.g.:
- you want to load a nested resource with all properties for easier export
- you have large/slow resolvers in some resources and don't want to load these for performance reasons
We therefore provide a way to customize the loaded field-set by defining fragments, blacklists and whitelists.
Additionaly you can use that to create "virtual resources" that show other fields.
Basic usage
using one fragment for both one and many:
buildGraphQLProvider({
clientOptions: { uri: "/api/graphql" } as any,
resourceViews: {
<local resource name>: {
resource: <backend resource name>,
fragment: <fragment to use>
},
},
});You can also specify different fragments for one and many (or ommit one of those):
buildGraphQLProvider({
clientOptions: { uri: "/api/graphql" } as any,
resourceViews: {
<local resource name>: {
resource: <backend resource name>,
fragment: {
one: <fragment for one>
many: <fragment for many>
}
},
},
});<backend resource name>: the name of an existingResourcethat is defined on the backend<local resource name>: if you use the same name as<backend resource name>, you basically customize thatResource. You can chose a different name to create a "virtual" resource.<fragment for ...>: Specifies which fields are loaded when fetching one or many records. See below for available fragment types- if you only specify
oneormany, it will fall back to the default behavior if either would be used.
Gotcha: when using different fragments for one and many, the detail view might be loaded with certain fields being undefined. See this discussion
Fragment type blacklist / whitelist
Use this if you want to exclude certain fields (blacklist) or only include certain fields:
// whitelist
buildGraphQLProvider({
clientOptions: { uri: "/api/graphql" } as any,
resourceViews: {
Users: {
resource: "Users",
fragment: {
many: {
type: "whitelist",
fields: ["id", "firstName", "lastName"],
},
},
},
},
});// blacklist
buildGraphQLProvider({
clientOptions: { uri: "/api/graphql" } as any,
resourceViews: {
Users: {
resource: "Users",
fragment: {
many: {
type: "blacklist",
fields: ["roles", "avatarImage"],
},
},
},
},
});Fragment type DocumentNode
You can use graphql Fragments (DocumentNode) to presicely select fields. This is more verbose than using blacklists / whitelists, but enables you to deeply select fields. Additionaly your IDE can typecheck the fragment (e.g. when using the apollo extension in vscode).
import gql from "graphql-tag";
buildGraphQLProvider({
clientOptions: { uri: "/api/graphql" } as any,
resourceViews: {
Users: {
resource: "Users",
fragment: {
many: gql`
fragment OneUserWithTwitter on User {
id
firstName
lastName
userSocialMedia {
twitter
}
}
`,
},
},
},
});Virtual Resources
You can use a different name for a resource, that does not exist on the backend:
// real world example
buildGraphQLProvider({
clientOptions: { uri: "/api/graphql" } as any,
resourceViews: {
ParticipantsToInvoice: {
resource: "ChallengeParticipation",
fragment: {
one: gql`
fragment OneBilling on ChallengeParticipation {
challenge {
title
}
user {
email
firstname
lastname
school {
name
address
city {
name
zipCode
canton {
id
}
}
}
}
teamsCount
teams {
name
}
}
`,
many: gql`
fragment ManyBillings on ChallengeParticipation {
challenge {
title
}
user {
email
firstname
lastname
school {
name
address
}
}
teams {
name
}
}
`,
},
},
},
});Now you have a new virtual resource ParticipantsToInvoice that can be used to display a List or for one record. (notice: update/create/delete is currently not specified, so use it read-only) and it will have exactly this data.
There are two ways you can use this new virtual resource. If you want to use it with React-Admin's query hooks (useQuery, useGetList, useGetOne), you need to add this as a new <Resource>:
<Admin>
// ...
<Resource name="ParticipantsToInvoice" />
</Admin>These hooks rely on Redux store and will throw an error if the resource isn't defined.
However, if you directly use data provider calls, you can use it with defined <Resource> but also without as it directly calls data provider.
const dataProvider = useDataProvider(options);
const { data } = await dataProvider.getList("ParticipantsToInvoice", {
pagination: { page: 1, perPage: 10 },
sort: { field: "id", order: "ASC" },
filter: {},
});Customize input data on create / update (experimental)
You can alter the data sent to create / update mutations.
E.g. Consider having a field <TextField source="userEmail" />. userEmail does not exist on the Resource,
but you want to use this field to customize the data sent to the backend:
const dataProvider = useDataProvider({
customizeInputData: {
MyResource: {
create: (data, params) => ({
...data,
user: {
connectOrCreate: {
create: {
email: params.userEmail,
},
where: {
email: params.userEmail,
},
},
},
}),
update: // ...
},
},
});Usage with typegraphql-prisma
(beta)
You can use the dataprovider to connect to a backend that was created using https://www.npmjs.com/package/typegraphql-prisma. It has a slightly different dialect. Pass the following option to the dataprovider:
const dataProvider = useDataProvider({
clientOptions: { uri: "/graphql" }
queryDialect: "typegraphql" // 👈
})2 years ago
2 years ago
2 years ago
2 years ago
2 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
5 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago
6 years ago