Разумная реакция - Часть 2: GraphQL
Звуковые, безопасные для типов взаимодействия с GraphQL
Я вернулся с более подробной информацией о том, как мы используем ReasonML и GraphQL для быстрого создания типобезопасных конечных точек GraphQL! Я долго искал стек, который мог бы использовать преимущества звука, выразительной печати, но при этом предоставлял бы мне гибкость и функциональную совместимость JavaScript. Наконец-то я нашел это в Reason!
Сегодня я собираюсь познакомить вас со своей клиентской структурой GraphQL. Я пользуюсь преимуществами некоторых выдающихся инструментов Reason, которые являются совершенно новыми и быстро меняются, так что будьте готовы к небольшому оттоку за кулисами. Однако, если вы можете немного подправить вещи при обновлении библиотек, тогда все будет в порядке!
Со временем я буду абстрагироваться от шаблонных, соединяющих элементы этой архитектуры, в структуру, которую легко использовать повторно. А пока я расскажу вам, как я в первую очередь добиваюсь рабочего процесса.
Типобезопасные запросы с graphql_ppx
Для начала я использую библиотеку graphql_ppx для автоматической проверки типов моих запросов GraphQL на основе схемы сервера. Эта библиотека включает изящный send-introspection-query
скрипт для получения схемы с сервера через HTTP-вызов. После выполнения инструкций по установке и получения схемы с сервера я готов написать свою первую мутацию:
Выражение [%graphql {| |}]
сообщает компилятору, что мы хотим применить graphql «ppx rewriter» (терминология OCaml для макроса или препроцессора) к многострочной строке, заключенной в синтаксис {| |}
. Это преобразует наш запрос в набор функций OCaml, которые могут обрабатывать все входные данные и данные ответа безопасным для типов способом.
В моем коде ReasonML я могу сразу начать работу с этим модулем, используя CreateEvent.make()
, функцию, автоматически написанную препроцессором.
Однако, чтобы использовать его в моем простом JavaScript, мне нужно сделать шаг по преобразованию беспорядочного, хаотичного ввода JS в более структурированный ввод OCaml.
С помощью приведенного выше кода я сначала открываю и присваиваю несколько значений, которые мне нужно будет использовать ниже в файле. Затем я устанавливаю свой тип ввода - беспорядочный JS, над которым я собираюсь действовать. Моя функция fromJs
принимает этот беспорядочный ввод и преобразует его в формат, с которым OCaml может справиться более эффективно.
Помимо операторов пользовательского уровня
Функция fromJs
использует Option.Infix
из BsAbstract
для введения оператора <#>
. Лучше всего использовать такие операторы пользовательского уровня в умеренных количествах, но я чувствовал, что это сделало код более читабельным, как только концепция будет понятна.
Одна из причин, по которой они разочарованы, заключается в том, что они не могут быть обнаружены во многих других языках с влиянием ML. В таких случаях часто бывает трудно понять, откуда на самом деле приходит оператор, и невозможно выполнить поиск в Google с помощью специальных символов. Благодаря OCaml и его слабому полиморфизму у нас есть хорошая навигационная цепочка, которую мы можем использовать, чтобы без особых проблем отследить определение. Мы обнаруживаем, что оператор <#>
определяет интерфейсы BsAbstract и относится к обратному отображению Functor. Сопоставление функторов поднимает функцию для работы с типом более высокого порядка, поэтому это означает, что я беру такую функцию, как Js.Json.string
, которая возвращает тип Js.Json.t
, и оборачиваю ее в Option, чтобы вместо этого она возвращала option(Js.Json.t)
.
Выполнение запроса с помощью Reason-Apollo
Результатом функции CreateEvent.make()
является объект запроса, который понимает библиотека reason-apollo. Чтобы использовать его, я передаю его модулю ApiClient
в моем проекте. Я использую этот модуль, а не встроенные компоненты, которые предоставляет reason-apollo
, поэтому я могу просто использовать его как обещание везде, где мне это нужно.
Вы можете увидеть весь этот модуль здесь, но пока это та часть, о которой вам нужно знать:
Модуль graphql_ppx
предоставляет инструмент для синтаксического анализа полученных данных в более удобную структуру в OCaml. Я использую функцию parseResponse
, чтобы добавить новое свойство к объекту JavaScript, возвращаемому функцией ApiClient
- «parsed». Это свойство содержит результаты данных после их передачи через функцию parse
.
Прямо сейчас у меня есть две функции - одна для запросов и одна для мутаций, но я, вероятно, мог бы без особых проблем объединить их в одну. Они используют экземпляр client
, созданный выше в файле, для отправки запроса или мутации и используют parseResponse
перед передачей ответа обратно вызывающему коду. Оператор <#>
снова используется для поднятия parseResponse
к типу Promise
.
Правильное использование EventData
Чтобы использовать получившийся интерфейс EventData
, который я создал выше в Reason, я могу напрямую использовать модуль CreateEvent
.
Оператор >>=
- это оператор Monad bind
, полученный из библиотеки BsAbstract
. Он придает новый виток строящемуся конвейеру Monadic. Функция setNewEventFromResponse
возвращает обещание, поэтому я могу bind
до конца последнего шага в конвейере - CreateEvent.make()
. Это позволяет мне добавить новый шаг в процесс, который запускается после завершения предыдущего шага и принимает результат предыдущего шага в качестве входных данных. Если вы говорите себе: «О! Это просто then
! " - Вы совершенно правы. Функция then
является функцией bind
для Promise
. Да, верно - обещание - это монада!
Выглядит сложно. Зачем мне это делать?
Безопасность звукового типа действительно требует большей предварительной работы, чем динамические типы. Я определенно заметил разницу во времени, которое у меня уходит на написание функции ReasonML, и во времени, которое уходит на написание простой функции JS. Также гораздо большая разница во времени, необходимом для тестирования и рефакторинга модуля после того, как я его написал.
Мой код JavaScript почти всегда ломается при первом тестировании. Я должен следить за журналами отладки и сообщениями об ошибках, чтобы определить, где он упал, внести изменения и повторить попытку. Это затрудняет оценку функций, потому что вы никогда не знаете, сколько времени может занять процесс тестирования и исправления ошибок. Вы далеки от завершения только потому, что закончили писать компонент или менеджер состояний.
С моим кодом Reason он часто просто работает ™ ️ в первый раз. Когда что-то ломается, часто легко увидеть ошибочное предположение о типах данных, которое является основной причиной сбоя. Часто тестирование проходит гладко и быстро заканчивается, даже когда возникают проблемы. Единственное исключение из этого правила - когда тестирование выявляет основное предположение, которое ложно, и мне приходится переписывать много кода. Я бы сделал то же открытие в JavaScript и должен был бы переписать код таким же образом, но обычно мне требуется больше времени, чтобы сделать те же определения в свободной, динамически типизированной среде.
Когда мне действительно нужно внести изменения, разумная полнота проверки типа Reason означает, что он с гораздо большей вероятностью определит места, на которые влияют мои изменения, чем что-то более простое, например Flow или TypeScript.
К счастью, такие инструменты, как graphql_ppx
, упрощают работу с этой структурой. Изучая схему GraphQL непосредственно с сервера и генерируя много шаблонного кода проверки типов и манипуляций, вы можете позволить своим данным говорить сами за себя.
На этом все закончится. Если вы хотите увидеть полный код проекта, зайдите в ecliptic / reason-events-web и проверьте его! Он включает в себя полный исходный код, как с API, так и с клиентом. Сервер использует отличный инструмент под названием postgraphile
, который автоматически генерирует интерфейс со схемой GraphQL на основе самоанализа над существующей базой данных Postgres.
Далее: состояние!
Это сложная тема, которая может быть затронута во многих местах, но в следующий раз я собираюсь связать все это вместе с Государственным управлением! Одна из выдающихся особенностей стека ReasonML заключается в том, насколько легко его постепенно внедрять. Я покажу вам, как я обрабатываю управление состоянием наиболее безопасным для типов способом, позволяя ему плавно работать с Redux, при этом используя преимущества вариантов и сопоставления с образцом.
А пока мы ищем новые консалтинговые проекты в Ecliptic! Если у вашей команды есть большая функция, новый продукт или переписанный продукт - объединитесь с нами в Ecliptic, чтобы это произошло! Мы можем помочь создать базовую архитектуру и передовые методы, в то же время доведя вашу команду до совершенства в стеке и помогая им повысить заинтересованность и скорость!
Свяжитесь со мной по адресу [email protected], если у вас есть вопросы или если вы заинтересованы в наших консультационных услугах! Спасибо за прочтение!