Разумная реакция - Часть 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], если у вас есть вопросы или если вы заинтересованы в наших консультационных услугах! Спасибо за прочтение!