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. &#8594

Hooks and components that support MetaDataQuery:

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

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