Введение
Технология GraphQL, в отличие от стандартной REST API, позволяет делать запросы на сервер на по множеству ендпоинтов, а по одному и для получения и изменения данных используются query и mutations. Попробуем создать простое приложение для демонстрации как это работает.
Cоздаем проект
yarn create react-app react-apollo-hasura --template typescript
Пока создается проект заходим на сайт hasura и создаем новый проект.

Создаем простую таблицу с телефонами. Добавляем поля id, name, image, description и price.
Переходим в закладку api.

Там есть ссылка на нашу апишку и хедеры, которые необходимы для доступа к ней.
Добавляем apollo client в проект
yarn add @apollo/client
Создадим файл createApolloClient.ts куда вписыаем
import {
ApolloClient,
InMemoryCache,
HttpLink,
} from "@apollo/client";
export const createApolloClient = () => {
return new ApolloClient({
link: new HttpLink({
uri: "https://living-osprey-95.hasura.app/v1/graphql",
headers: {
'content-type': 'application/json',
'x-hasura-admin-secret': `************`,
},
}),
cache: new InMemoryCache(),
});
};
туда мы пишем нашу ссылку на апишку и хеддеры.
Для UI я использовал charka.io.
Создаем папку Components где добавим файл types.ts
export type GoodType = {
id: number
name: string
price: number
image: string
description: string | null
}
export type GoodTypeFromQuery = {
id?: number
name?: string
price?: number
image?: string
description?: string | null
}
А также файл queries.ts
import { gql } from "@apollo/client";
import { GoodType } from "./types";
export const getGoodsQuery = (fields: Array<keyof GoodType>) => {
return gql`
query {
goods {
${fields.join(',\n')}
}
}
`
}
В файле App.ts добавим ApolloProvider а также стейт ключей, которые мы сможем изменять, чтобы продемонстрировать работу GraphQL
import React, { useCallback, useState } from "react";
import { createApolloClient } from "./createApolloClient";
import { ApolloProvider } from "@apollo/client";
import { Box, Heading, Stack } from "@chakra-ui/react";
import Goods from "./Components/Goods";
import { GoodType } from "./Components/types";
import Control from "./Components/Control";
function App() {
const [client] = useState(createApolloClient());
const keys: Array<keyof GoodType> = [
"id",
"name",
"price",
"image",
"description",
];
const [fields, setFields] = useState<Array<keyof GoodType>>(keys);
const setField = useCallback(
({ key, active }: { key: keyof GoodType; active: boolean }) => {
if (active) {
if (!fields.includes(key)) {
setFields([...fields, key]);
}
} else {
setFields(fields.filter((el) => el !== key));
}
},
[fields, setFields]
);
return (
<ApolloProvider client={client}>
<Box m={[5, 5]}>
<Heading>HASURA GRAPHQL EXAMPLE</Heading>
</Box>
<Box m={[20, 5]}>
{keys.map((el, idx) => (
<Control
key={idx}
_key={el}
isActive={fields.includes(el)}
setField={setField}
/>
))}
</Box>
<Stack direction="column">
<Goods fields={fields} />
</Stack>
</ApolloProvider>
);
}
export default App;
В компоненте Goods.tsx мы будем получать данные товаров в зависимости от активных ключей, которые передаются в компонент
import { useQuery } from "@apollo/client";
import { getGoodsQuery } from "./queries";
import { CircularProgress, Stack, Text, StackDivider } from "@chakra-ui/react";
import { GoodType, GoodTypeFromQuery } from "./types";
import Good from "./Good";
import { Fragment, useEffect } from "react";
interface Props {
fields: Array<keyof GoodType>;
}
export default function Goods({ fields }: Props) {
const { loading, error, data, refetch } = useQuery<{ goods : GoodTypeFromQuery[]}>(
getGoodsQuery(fields)
);
useEffect(() => {
refetch()
}, [fields])
console.log(fields);
return (
<Stack
direction="column"
m={[10, 10]}
divider={<StackDivider borderColor="gray.300" />}
>
{loading && <CircularProgress isIndeterminate color="green.300" />}
{error && (
<Text fontSize={"40px"} color="tomato">
{error.message}
</Text>
)}
{data && (
<Fragment>
{data.goods.map((el) => (
<Good key={el.id} data={el} />
))}
</Fragment>
)}
</Stack>
);
}
Компонент одного товара Good.tsx
import { Center, Image, Text, VStack } from "@chakra-ui/react";
import { getPriceString } from "../helpers/getPriceString";
import { GoodTypeFromQuery } from "./types";
export default function Good({ data }: { data: GoodTypeFromQuery }) {
return (
<Center border={"1px solid gray"} marginBottom={10} p={[10, 10]}>
{data.name && <Text fontWeight={700} marginRight={10}>
{data.name}
</Text>}
{data.image && <Image src={data.image} w="50px" h="50px" m={[5, 5]}/>}
{data.price && <Text m={[5, 5]}>{getPriceString(data.price)}</Text>}
{data.description && <Text>{data.description}</Text>}
</Center>
);
}
Control.tsx
import { Checkbox } from "@chakra-ui/react";
import { GoodType } from "./types";
export default function Control({
setField,
_key,
isActive,
}: {
setField: (data: { key: keyof GoodType; active: boolean }) => void;
isActive: boolean;
_key: keyof GoodType;
}) {
return (
<Checkbox
colorScheme={"green"}
checked={isActive}
defaultChecked
onChange={() => {
setField({ key:_key, active: !isActive });
}}
m={[2, 2]}
>
{_key}
</Checkbox>
);
}
Весь фокус в том, что изначально мы получаем все поля товаров.


Вся штука в том, что если мы уберем галочки с некоторых полей, то данные мы получим уже без них.


Вот и вся наука. GraphQL делает работу с апишкой приятной и удобной, а apollo/client под капотом делает все грязную работу за нас.