├── .gitignore
├── .prettierrc
├── .vscode
└── settings.json
├── README.md
├── package.json
├── public
├── favicon.ico
├── index.html
├── logo192.png
├── logo512.png
├── manifest.json
└── robots.txt
├── src
├── App.tsx
├── custom-build-fields.ts
├── index.css
├── index.tsx
├── queries
│ ├── todos.ts
│ └── users.ts
├── react-admin.d.ts
├── react-app-env.d.ts
└── resources
│ ├── todos
│ ├── Create.tsx
│ ├── Edit.tsx
│ ├── List.tsx
│ └── index.ts
│ └── users
│ ├── List.tsx
│ ├── Show.tsx
│ └── index.ts
├── tsconfig.json
└── yarn.lock
/.gitignore:
--------------------------------------------------------------------------------
1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
2 |
3 | # dependencies
4 | /node_modules
5 | /.pnp
6 | .pnp.js
7 |
8 | # testing
9 | /coverage
10 |
11 | # production
12 | /build
13 |
14 | # misc
15 | .DS_Store
16 | .env.local
17 | .env.development.local
18 | .env.test.local
19 | .env.production.local
20 |
21 | npm-debug.log*
22 | yarn-debug.log*
23 | yarn-error.log*
24 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | {
2 | "endOfLine": "lf",
3 | "semi": true,
4 | "singleQuote": true,
5 | "tabWidth": 2,
6 | "trailingComma": "es5"
7 | }
8 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.formatOnSave": true
3 | }
4 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Admin x Hasura
2 |
3 | This is a demo application showing how to use [ra-data-hasura](https://github.com/hasura/ra-data-hasura) to build a [react-admin](https://marmelab.com/react-admin/) application backed by a [Hasura](https://hasura.io/) GraphQL API.
4 |
5 | You can take a look at the app here.
6 |
7 | It uses [ra-data-hasura](https://github.com/hasura/ra-data-hasura) as the React Admin [Data Provider](https://marmelab.com/react-admin/DataProviders.html), and focusses on showing how to write **completely custom GraphQL queries** - just as GraphQL should be used!
8 |
9 | This repository is forked from [react-admin-low-code](https://github.com/cpursley/react-admin-low-code), using the same Hasura GraphQL backend, but allowing for custom queries to be specified by the React Admin client instead of relying on the default generated queries.
10 |
11 | ## Getting started
12 |
13 | 1. `yarn` to install dependencies
14 | 2. `yarn start` to run the application at `localhost:3000`
15 | 3. (Optional): toggle between branches: `custom-queries` and `extending-queries` (master is equal to `custom-queries`)
16 |
17 | ## How it works
18 |
19 | This demo shows two ways to customise the GraphQL queries sent from your React Admin client to a Hasura backend:
20 |
21 | 1. Defining completely custom `gql` queries - shown by the `custom-queries` branch (same as `master` branch)
22 | 2. Extending the default React Admin queries - shown by the `extending-queries` branch
23 |
24 | Each method has a `custom-build-fields.ts` file which does the same 3 things:
25 |
26 | 1. Defines an `extractFieldsFromQuery` function which can extract just the fields from a GraphQL AST generated by a `gql` query:
27 |
28 | ```js
29 | const extractFieldsFromQuery = (queryAst) => {
30 | return queryAst.definitions[0].selectionSet.selections;
31 | };
32 | ```
33 |
34 | 2. Imports the custom queries defined in `src/queries` directory; these custom queries are defined as `gql` template literals
35 | 3. Defines a `customBuildFields` function which applies the custom queries for the relevant resource and fetch types; this function is passed to the `buildHasuraProvider` from `ra-data-hasura-graphql`.
36 |
37 | ## Troubleshooting
38 |
39 | React Admin assumes that a resouces's `GET_LIST` query will return the same fields as the corresponding `GET_ONE` query for that resource. This allows React Admin to using a caching system whereby if a resource has already been fetched by it's `List` view, then the corresponding `Show` view can read the record from the Redux store whilst the fetch for the individual record is loading.
40 |
41 | Using `ra-data-hasura-graphql` in the way we demonstrate here means that it's possible for a `GET_LIST` query fields to differ from from the `GET_ONE` query fields. This could cause React Admin to try and read a field that does not immediately exist on a record.
42 |
43 | For example, if a `todos` resource has the following custom queries defined:
44 |
45 | ```js
46 | const GET_LIST_TODOS = gql`
47 | {
48 | id
49 | title
50 | is_completed
51 | }
52 | `;
53 |
54 | const GET_ONE_TODO = gql`
55 | {
56 | id
57 | title
58 | is_completed
59 | user_id
60 | }
61 | `;
62 | ```
63 |
64 | Loading a `List` of todos will populate Redux with records that have 3 fields: `id`, `title`, `is_completed`. Navigating to a `Show` view for a todo record will then result in React Admin trying to find a `user_id` field, but this will not exist until the `GET_ONE_TODO` query has resolved.
65 |
66 | ### How to avoid this problem
67 |
68 | There are 2 ways to avoid this problem:
69 |
70 | 1. Ensure that resources have consistent fields for `GET_LIST` and `GET_ONE` queries.
71 | 2. Handle loading states for fields that might not exist until the another query has resolved, for example:
72 |
73 | ```jsx
74 | import { FunctionField } from 'react-admin';
75 |
76 | {
79 | if (!record.user_id) return