Если вам понравилась эта статья, поддержите меня, проверив Pull Reminders, бота Slack, который отправляет вашей команде автоматические напоминания о запросах на вытягивание GitHub.

Одна из проблем, с которой я недавно столкнулся при работе с GraphQL и React, заключалась в том, как обрабатывать ошибки. Как разработчики, мы, вероятно, раньше реализовывали страницы 500, 404 и 403 по умолчанию в серверных приложениях, но выяснить, как это сделать с помощью React и GraphQL, сложно.

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

Фон

Проект, над которым я работал, представлял собой довольно типичное приложение CRUD, созданное на React с использованием GraphQL, Apollo Client и express-graphQL. Мы хотели обрабатывать определенные типы ошибок - например, отказ сервера - путем отображения пользователю стандартной страницы с ошибкой.

Наша первоначальная задача заключалась в том, чтобы найти лучший способ сообщить клиенту об ошибках. GraphQL не использует коды состояния HTTP, такие как 500, 400 и 403. Вместо этого ответы содержат массив errors со списком ошибок (подробнее о errors в Спецификации GraphQL).

Например, вот как выглядел наш ответ GraphQL, когда что-то сломалось на сервере:

Поскольку ответы GraphQL об ошибках возвращают код состояния HTTP 200, единственный способ определить тип ошибки - это проверить массив errors. Это казалось плохим подходом, потому что свойство error message содержало исключение, сгенерированное на сервере. Спецификация GraphQL утверждает, что значение message предназначено для разработчиков, но не указывает, должно ли это значение быть удобочитаемым сообщением или чем-то, предназначенным для программной обработки:

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

Добавление кодов ошибок в ответы GraphQL

Чтобы решить эту проблему, мы добавили стандартизированные коды ошибок к нашим объектам ошибок, которые могли использоваться клиентами для программного определения ошибок. Это было вдохновлено тем, как Stripe REST API возвращает строковые коды ошибок в дополнение к понятным для человека сообщениям.

Мы решили начать с трех кодов ошибок: authentication_error, resource_not_found и server_error.

Чтобы добавить их в наши ответы GraphQL, мы передали нашу собственную formatError функцию в express-graphql, которая сопоставляет исключения, создаваемые на сервере, со стандартными кодами, которые добавляются в ответ. Спецификация GraphQL обычно не рекомендует добавлять свойства к объектам ошибок, но допускает это, вкладывая эти записи в объект extensions.

Наши ошибки ответа GraphQL было легко классифицировать:

Хотя мы разработали собственный способ добавления кода к ответам, генерируемым express-graphql, apollo-server, похоже, предлагает аналогичное встроенное поведение.

Отображение страниц ошибок с границами ошибок React

Как только мы выяснили, как правильно обрабатывать ошибки на нашем сервере, мы обратили свое внимание на клиента.

По умолчанию мы хотели, чтобы наше приложение отображало страницу глобальной ошибки (например, страницу с сообщением «ой что-то пошло не так») всякий раз, когда мы встречаем server_error, authorization_error или authorization_not_found. Однако нам также нужна была гибкость, позволяющая при желании обрабатывать ошибку в конкретном компоненте.

Например, если пользователь вводил что-то в строку поиска и что-то пошло не так, мы хотели отображать сообщение об ошибке в контексте, а не переходить на страницу с ошибкой.

Для этого мы сначала создали компонент под названием GraphqlErrorHandler, который будет располагаться между компонентами apollo-client, Query и Mutation и их дочерними элементами, подлежащими рендерингу. Этот компонент, проверявший коды ошибок в ответе, вызывал исключение, если он идентифицировал код, который нам небезразличен:

Чтобы использовать GraphqlErrorHandler, мы обернули компоненты Query и Mutation apollo-client:

Затем наш функциональный компонент использовал наш собственный Query компонент вместо прямого доступа к react-apollo:

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

Как вы помните, ранее наша цель состояла в том, чтобы по умолчанию отображать страницы с глобальными ошибками (например, страницу с сообщением «ой что-то пошло не так»), но при этом у нас есть возможность обрабатывать ошибку локально в любом компоненте, если мы захотим.

Границы ошибок React предоставляют фантастический способ сделать это. Границы ошибок - это компоненты React, которые могут перехватывать ошибки JavaScript в любом месте своего дочернего дерева компонентов, чтобы вы могли обрабатывать их с помощью настраиваемого поведения.

Мы создали границу ошибки под названием GraphqlErrorBoundary, которая будет перехватывать любые связанные с сервером исключения и отображать соответствующую страницу ошибки:

Мы используем границу ошибки в качестве оболочки для всех компонентов нашего приложения:

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

Например, вот как это выглядело бы, если бы мы хотели, чтобы в нашем компоненте использовалось настраиваемое поведение обработки ошибок, как было раньше:

Заворачивать

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