Skip to main content
Version: 2.xx.xx

GraphQL

refine graphql and strapi-graphql data provider built with gql-query-builder and graphql-request is made for GraphQL implemantation. 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 @pankod/refine @pankod/refine-strapi-graphql graphql-request
info

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

Usage

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

src/App.tsx
import { Refine } from "@pankod/refine";
import routerProvider from "@pankod/refine-react-router";
import dataProvider from "@pankod/refine-strapi-graphql";
import { GraphQLClient } from "graphql-request";

const client = new GraphQLClient("API_URL");

const App: React.FC = () => {
return (
<Refine
routerProvider={routerProvider}
dataProvider={dataProvider(client)}
/>
);
};
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 metaData to hooks that we will fetch data from.

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

const { selectProps } = useSelect<ICategory>({
resource: "categories",
metaData: {
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 metaData to determine which fields will come in this request.

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

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

const [selectedTab, setSelectedTab] =
useState<"write" | "preview">("write");

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,
},
]}
>
<ReactMde
selectedTab={selectedTab}
onTabChange={setSelectedTab}
generateMarkdownPreview={(markdown) =>
Promise.resolve(
<ReactMarkdown>{markdown}</ReactMarkdown>,
)
}
/>
</Form.Item>
</Form>
</Edit>
);
};
info

The create page is largely the same as the edit page, there is no need to pass metaData 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 metaData.

GraphQL edit page

Show Page

Show component includes the <RefreshButton> by default. We can pass refetch method of queryResult to it's 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<IResourceComponentsProps> = () => {
const { queryResult } = useShow<IPost>({
resource: "posts",
metaData:{
fields: [
"id",
"title",
{
category: ["title"],
},
"content",
],
},
});
const { data, isLoading } = queryResult;
const record = data?.data;

return (
<Show
isLoading={isLoading}
pageHeaderProps={{ 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