10 часто задаваемых вопросов по GraphQL

Источник: «Your 10 Most Common GraphQL Questions Answered»
В этой статье мы ответим на десять часто задаваемых вопросов о GraphQL, включая разбиение на страницы, управления версиями GraphQL, пакетную загрузку, обработку кэша, загрузку файлов и многое другое.

1. Как обрабатывать ошибки в GraphQL

В GraphQL ошибки обрабатываются путём возврата поля error в ответе. Поле error представляет собой массив объектов ошибок, каждый из которых содержит поле message, и возможно, другие поля с дополнительной информацией.

Для обработки ошибок на стороне сервера, вы можете создавать собственные ошибки в резолверах. Пример на JavaScript:

throw new Error('Something went wrong');

На стороне клиента вы можете проверить наличия поля error в ответе и соответствующим образом обработать их.

2. Как разбивать результаты на страницы в GraphQL

Чтобы разбить результаты на страницы в GraphQL, вы можете использовать шаблон Соединение включающий использование рёбер и узлов для представления соединений между объектами. Вы также можете использовать такие аргументы, как first, last, before и after, для управления нумерацией страниц.

Вот пример схемы для разбивки списка пользователей на страницы:

type Query {
users(first: Int, after: String): UserConnection
}

type UserConnection {
edges: [UserEdge]
pageInfo: PageInfo
}

type UserEdge {
node: User
cursor: String
}

type PageInfo {
hasNextPage: Boolean
endCursor: String
}

В резолвере вы должны реализовать логику для извлечения данных с разбивкой на страницы и возврата соответствующего объекта соединения.

3. Как обрабатывать аутентификацию и авторизацию в GraphQL

В GraphQL нет встроенной аутентификации и авторизации, но вы можете реализовать их с помощью middleware или контекста. Для аутентификации можете использовать подход на основе токенов (JWT) или любой другой механизм аутентификации.

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

Пример на JavaScript

// Middleware для проверки токена аутентификации
const authenticationMiddleware = async (req, res, next) => {
const token = req.headers.authorization;
const user = await verifyToken(token);
req.user = user;
next();
};

// Добавить аутентифицированного пользователя в контекст
const context = ({ req }) => {
return { user: req.user };
};

// Проверка аутентификации в резолвере
const resolver = {
Query: {
protectedData: (parent, args, context) => {
if (!context.user) {
throw new Error('Not authenticated');
}
// Fetch and return the protected data
},
},
};

4. Как обрабатывать обновления в реальном времени с GraphQL

Для обработки обновлений в реальном времени в GraphQL вы можете использовать подписки. Подписки позволяют клиентам получать обновления, когда на сервере происходят определённые события.

Для реализации подписок, вам необходимо определить тип Subscription в схеме и использовать поле subscription в резолверах, чтобы определить события запускающие обновления.

Например:

type Subscription {
userCreated: User
}

В резолвере вы можете использовать эмиттер событий или pub/sub систему для обработки подписок:

const { PubSub } = require('graphql-subscriptions');
const pubsub = new PubSub();

const USER_CREATED = 'USER_CREATED';

const resolvers = {
Subscription: {
userCreated: {
subscribe: () => pubsub.asyncIterator(USER_CREATED),
},
},
Mutation: {
createUser: (parent, args) => {
const newUser = createUser(args);
pubsub.publish(USER_CREATED, { userCreated: newUser });
return newUser;
},
},
};

5. Как обрабатывать загрузку файлов с GraphQL

GraphQl не имеет встроенной поддержки загрузки файлов, но вы можете использовать пакет graphql-upload для обработки загрузки файлов на сервер GraphQL.

Сначала установите пакет:

npm install graphql-upload

Затем добавьте скаляр Upload в вашу схему:

scalar Upload

type Mutation {
uploadFile(file: Upload!): File
}

В резолвере можете использовать метод createReadStream для обработки загруженного файла:

const { GraphQLUpload } = require('graphql-upload');

const resolvers = {
Upload: GraphQLUpload,
Mutation: {
uploadFile: async (parent, { file }) => {
const { createReadStream, filename, mimetype } = await file;
// Обработка загруженного файла (например, сохранение его на диск или облачное хранилище)
return { filename, mimetype };
},
},
};

6. Как работать с кэшированием в GraphQL

Кэширование GraphQL может быть реализовано как на стороне клиента, так и на стороне сервера. На стороне клиента можно использовать такие библиотеки, как Apollo Client или Relay, представляющие встроенные механизмы кэширования.

На стороне сервера можно реализовать кэширование с помощью DataLoader, утилиты, предоставляемой Facebook, которая помогает выполнять пакетную обработку и кэширование операций выборки данных. DataLoader можно использовать для кэширования запросов к базе данных, вызовов API или любых других операций по выборке данных.

Сначала установите DataLoader:

npm install dataloader

Затем создайте экземпляр DataLoader для каждой операции выборки данных, которую хотите кэшировать:

const DataLoader = require('dataloader');

const userLoader = new DataLoader(async (userIds) => {
const users = await getUsersByIds(userIds);
return userIds.map((id) => users.find((user) => user.id === id));
});

В резолверах используйте экземпляр DataLoader для извлечения данных:

const resolvers = {
Query: {
user: (parent, { id }) => userLoader.load(id),
},
};

7. Как реализовать пакетную загрузку в GraphQL

Пакетную загрузку можно реализовать с помощью DataLoader, который помогает выполнять пакетную обработку и кэширование операций выборки данных. DataLoader группирует несколько запросов для одного и того же типа данных в один пакет, уменьшая количество запросов к базе данных или вызовов API.

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

8. Как справляться с проблемами запросов N+1в GraphQL

Проблема N+1 запроса возникает, когда выборка связанных данных выполняет несколько запросов, что приводит к неэффективной выборке данных. DataLoader может помочь решить проблему запроса N+1, группируя и кэшируя операции выборки данных.

Используя DataLoader в резолверах, вы можете гарантировать, что связанные данные извлекаются в одном пакете, уменьшая количество и повышая производительность.

9. Как обрабатывать stitching и federation схем в GraphQL

Stitching и federation схем — это методы, используемы для объединения нескольких схем GraphQL в одну схему.

Stitching схемы может быть реализован с помощью пакета graphql-tools. Сначала установите пакет:

npm install graphql-tools

Затем используйте функцию mergeSchemas для комбинирования схем:

const { mergeSchemas } = require('graphql-tools');

const schema1 = makeExecutableSchema({ typeDefs: typeDefs1, resolvers: resolvers1 });
const schema2 = makeExecutableSchema({ typeDefs: typeDefs2, resolvers: resolvers2 });

const mergedSchema = mergeSchemas({ schemas: [schema1, schema2] });

Federation схем можно реализовать с помощью Apollo Federation. Сначала установите необходимые пакеты @apollo/federation и @apollo/gateway:

npm install @apollo/federation @apollo/gateway

Затем используйте функцию buildFederatedSchema для создания federated схемы для каждого сервиса:

const { buildFederatedSchema } = require('@apollo/federation');

const schema1 = buildFederatedSchema([{ typeDefs: typeDefs1, resolvers: resolvers1 }]);
const schema2 = buildFederatedSchema([{ typeDefs: typeDefs2, resolvers: resolvers2 }]);

Используйте класс ApolloGateway для создания шлюза, объединяющего federated схемы:

const { ApolloGateway } = require('@apollo/gateway');

const gateway = new ApolloGateway({
serviceList: [
{ name: 'service1', url: 'http://localhost:4001' },
{ name: 'service2', url: 'http://localhost:4002' },
],
});

10. Как управлять версиями в GraphQL

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

Для управления устареванием поля или аргумента, можете использовать директиву deprecationReason:

type User {
id: ID!
name: String!
email: String @deprecated(reason: "Use 'username' instead")
}

Развивая схему и используя deprecated, вы можете управлять версиями, не нарушая работу существующих клиентов.

Дополнительные материалы

Предыдущая Статья

Как управлять часовыми поясами в PHP

Следующая Статья

JavaScript: Освоение оператора switch