Skip to main content
Version: 4.xx.xx

GraphQL

refine graphql and strapi-graphql data provider built with gql-query-builder and graphql-request is made for GraphQL implementation. It aims to create a query dynamically with gql-query-builder and send requests with graphql-request.

GraphQL Query Builder​

GraphQL Query Builder allows us to build queries and mutations. The getList, getMany, and, getOne methods in our dataProvider generate a query to send a request. On the other hand, the create, createMany, update, updateMany, deleteOne, and, deleteMany methods generate a mutation to send a request.

In order to create a GraphQL query, our dataProvider has to take some options, such as specifying which fields will come in response, we pass these options to our dataProvider methods with MetaDataQuery.

Refer to the MetaDataQuery properties for detailed usage. β†’

Hooks and components that support MetaDataQuery:

Supported data hooksSupported other hooksSupported components
useUpdate β†’useForm β†’DeleteButton β†’
useUpdateMany β†’useModalForm β†’RefreshButton β†’
useDelete β†’useDrawerForm β†’
useDeleteMany β†’useStepsForm β†’
useCreate β†’useTable β†’
useCreateMany β†’useEditableTable β†’
useList β†’useSimpleList β†’
useOne β†’useShow β†’
useMany β†’useExport β†’
useCustom β†’useCheckboxGroup β†’
useSelect β†’
useRadioGroup β†’

Setup​

npm i @refinedev/core @refinedev/antd @refinedev/strapi-graphql
caution

To make this example more visual, we used the @refinedev/antd package. If you are using Refine headless, you need to provide the components, hooks or helpers imported from the @refinedev/antd package.

info

We used strapi-graphql server for this guide. You can customize your data provider for your own GraphQL server.

Usage​

To activate the data provider in @refinedev/strapi-graphql, we have to pass the API address with GraphQLClient.

src/App.tsx
import { Refine } from "@refinedev/core";
import {
Layout,
ReadyPage,
notificationProvider,
ErrorComponent,
} from "@refinedev/antd";
import routerProvider from "@refinedev/react-router-v6";
import dataProvider, { GraphQLClient } from "@refinedev/strapi-graphql";

import { BrowserRouter, Routes, Route, Outlet } from "react-router-dom";

const client = new GraphQLClient("API_URL");

const App: React.FC = () => {
return (
<BrowserRouter>
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider(client)}
notificationProvider={notificationProvider}
>
{/* ... */}
</Refine>
</BrowserRouter>
);
};
note

With GraphQLClient you can do things like add headers for authentication. On the other hand, you can send a request with your query.

Create Collections​

We created two collections on Strapi as posts and categories and added a relation between them. For detailed information on how to create a collection, you can check here.

You can see the fields of the collections we created as below.

post
{
"id": 1,
"title": "Eius ea autem.",
"content": "Explicabo nihil delectus. Nam aliquid sunt numquam...",
"category": {
"id": 24,
"title": "Placeat fuga"
}
}

List Page​

When sending the request, we must specify which fields will come, so we send fields in meta to hooks that we will fetch data from.

export const PostList: React.FC = () => {
const { tableProps, sorter } = useTable<IPost>({
sorters: {
initial: [
{
field: "id",
order: "asc",
},
],
},
meta: {
fields: [
"id",
"title",
{
category: ["title"],
},
],
},
});

const { selectProps } = useSelect<ICategory>({
resource: "categories",
meta: {
fields: ["id", "title"],
},
});

return (
<List>
<Table {...tableProps} rowKey="id">
<Table.Column
dataIndex="id"
title="ID"
sorter={{ multiple: 2 }}
defaultSortOrder={getDefaultSortOrder("id", sorter)}
/>
<Table.Column
key="title"
dataIndex="title"
title="Title"
sorter={{ multiple: 1 }}
/>
<Table.Column<IPost>
dataIndex="category"
title="Category"
filterDropdown={(props) => (
<FilterDropdown {...props}>
<Select
style={{ minWidth: 200 }}
mode="multiple"
placeholder="Select Category"
{...selectProps}
/>
</FilterDropdown>
)}
render={(_, record) => record.category.title}
/>
<Table.Column<IPost>
title="Actions"
dataIndex="actions"
render={(_, record) => (
<Space>
<EditButton
hideText
size="small"
recordItemId={record.id}
/>
<ShowButton
hideText
size="small"
recordItemId={record.id}
/>
<DeleteButton
hideText
size="small"
recordItemId={record.id}
/>
</Space>
)}
/>
</Table>
</List>
);
};
GraphQL list page

Edit Page​

On the edit page, useForm sends a request with the record id to fill the form. fields must be sent in meta to determine which fields will come in this request.

export const PostEdit: React.FC = () => {
const { formProps, saveButtonProps, queryResult } = useForm<
IPost,
HttpError,
IPost
>({
meta: {
fields: [
"id",
"title",
{
category: ["id", "title"],
},
"content",
],
},
});

const postData = queryResult?.data?.data;
const { selectProps: categorySelectProps } = useSelect<ICategory>({
resource: "categories",
defaultValue: postData?.category.id,
meta: {
fields: ["id", "title"],
},
});

return (
<Edit saveButtonProps={saveButtonProps}>
<Form
{...formProps}
layout="vertical"
onFinish={(values) =>
formProps.onFinish?.({
...values,
category: values.category.id,
} as any)
}
>
<Form.Item
label="Title"
name="title"
rules={[
{
required: true,
},
]}
>
<Input />
</Form.Item>
<Form.Item
label="Category"
name={["category", "id"]}
rules={[
{
required: true,
},
]}
>
<Select {...categorySelectProps} />
</Form.Item>
<Form.Item
label="Content"
name="content"
rules={[
{
required: true,
},
]}
>
<MDEditor data-color-mode="light" />
</Form.Item>
</Form>
</Edit>
);
};
info

The create page is largely the same as the edit page, there is no need to pass meta to useForm on the create page. If you want to use the created record as a response after the request, you can pass the fields with meta.

GraphQL edit page

Show Page​

<Show> component includes the <RefreshButton> by default. We can pass refetch method of queryResult to its onClick. This method repeats the last request made by the query. So it refreshes the data that is shown in page.

export const PostShow: React.FC = () => {
const { queryResult } = useShow<IPost>({
resource: "posts",
meta: {
fields: [
"id",
"title",
{
category: ["title"],
},
"content",
],
},
});
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<Show
isLoading={isLoading}
headerProps={{
extra: <RefreshButton onClick={() => queryResult.refetch()} />,
}}
>
<Title level={5}>Id</Title>
<Text>{record?.id}</Text>

<Title level={5}>Title</Title>
<Text>{record?.title}</Text>

<Title level={5}>Category</Title>
<Text>{record?.category.title}</Text>

<Title level={5}>Content</Title>
<MarkdownField value={record?.content} />
</Show>
);
};
GraphQL show page

Example​

RUN IN YOUR LOCAL
npm create refine-app@latest -- --example data-provider-strapi-graphql