Это часть 1, вы также можете найти часть 2 здесь или часть 3 здесь.
Недавно мы решили обновить один из наших микросервисов с помощью GraphQL. Приложение находится между нашим новым мобильным веб-клиентом и нашим сервисом Discovery API. Цель приложения - моделировать наши данные и повышать производительность за счет управления количеством обращений к сервису, которые клиент должен сделать во внешнем интерфейсе. Мы используем пограничное кэширование, чтобы избежать ненужных обращений к серверу, позволяя перехватывать запросы и раньше возвращать их из памяти. Мы были хорошо осведомлены о запросах, которые клиент будет отправлять заранее, поэтому мы также хотели использовать сохраненные запросы GraphQL в попытке сэкономить полосу пропускания между клиентом и сервером. Поскольку GraphQL отправляет запросы POST в одну конечную точку, кэширование на границе становится затруднительным. Здесь нам может помочь запрос GET. В этом руководстве мы создадим сервер GraphQL, который принимает запросы GET для постоянных запросов.
ОБНОВЛЕНИЕ: мир технологий быстро развивается. Эта серия руководств начинается с Apollo Server 1.3.2, но не беспокойтесь, в третьей части мы рассмотрим Apollo Server 2 и весь процесс миграции. А пока, если у вас возникнут какие-либо проблемы, вы можете скачать исходные файлы проекта здесь: исходные файлы.
Для начала создайте рабочий каталог и установите следующие пакеты с помощью npm.
npm init -y && npm install --save graphql graphql-tools apollo-server-express express graphql-playground-middleware-express body-parser ramda
Нам потребуется создать файл схемы и файл преобразователей.
touch schemas.js resolvers.js
schemas.js
const Greeting = ` type Greeting { name: String text: String } ` const Query = ` type Query { greeting(name: String): Greeting } ` module.exports = [Greeting, Query]
resolvers.js
const resolvers = { Query: { greeting: (_, { name }) => ({ name, text: 'How are you today?' }) } } module.exports = resolvers
Далее нам нужно создать извлеченный файл запросов. Это будет использоваться в качестве хэш-карты для клиента. Вместо того, чтобы отправлять весь документ запроса, мы позволим клиенту отправлять параметры запроса. Параметры запроса будут содержать ключ, называемый хешем, значение которого будет соответствовать ключу в нашем извлеченном файле запросов. Другими словами, вместо того, чтобы заказывать гамбургер, картофель фри и напиток, наш клиент просто заказывает номер 1. { hash: 1 }
touch extracted_queries.js
Внутри файла extract_queries.js мы экспортируем объект. Ключом будет хэш (в нашем случае простое целое число), а значением будет желаемый запрос.
extract_queries.js
module.exports = { 1: `query Greeting($name: String!) { greeting(name: $name) { name text } }` }
Затем нам нужно будет создать промежуточное программное обеспечение, чтобы проверять все входящие запросы на наличие хэша в параметрах запроса.
touch persistedQueries.js
Если хеш существует, мы получим соответствующий ключ в нашем извлеченном файле запросов. Если его нет, мы отправим запрос без изменений.
persistedQueries.js
const { omit } = require('ramda') const queryMap = require('./extracted_queries.js') const persistedQueries = (req, res, next) => { const { hash = '' } = req.query if (!hash) return next() const query = queryMap[hash] if (!query) { res.status(400).json({ error: [{}] }) return next(new Error('Invalid query hash')) } req.query = { query, variables: omit(['hash'], req.query) } next() } module.exports = persistedQueries
В функции persistedQueries мы начинаем с деструктуризации хэш-свойства объекта req.query, устанавливая в качестве значения по умолчанию пустую строку. Затем мы проверяем истинное значение в хэш-опоре, если она не указана, мы просто возвращаем next()
, отправляя запрос следующей следующей функции промежуточного программного обеспечения.
Если хэш существует, мы используем его для сопоставления с нашим извлеченным файлом запросов. Мы переназначаем объекту req.query соответствующий запрос и используем метод omit ramda, чтобы удалить хеш-ключ перед вызовом next()
, отправив запрос по пути. Если совпадения не существует, мы возвращаем статус 400 и вызываем next(...)
, передавая сообщение об ошибке, которое позже будет зарегистрировано и просмотрено. Теперь все, что нам нужно сделать, это создать файл server.js, чтобы связать все это вместе.
touch server.js
server.js
const express = require('express') const bodyParser = require('body-parser') const playground = require('graphql-playground-middleware-express').default const { graphqlExpress, graphiqlExpress } = require('apollo-server-express') const { makeExecutableSchema } = require('graphql-tools') const persistedQueries = require('./persistedQueries') const typeDefs = require('./schemas') const resolvers = require('./resolvers') const port = 4000 const app = express() const schema = makeExecutableSchema({ typeDefs, resolvers }) app.use( '/graphql', bodyParser.json(), persistedQueries, graphqlExpress({ schema }) ) app.use( '/playground', playground({ endpoint: '/graphql' }) ) app.listen(port, () => console.log(`listening on port: ${port}`))
Мы создаем простое экспресс-приложение и объединяем наши typeDefs и резолверы с помощью функции makeExecutableSchema graphql-tools. Мы используем express app.use
method для объявления промежуточного программного обеспечения на основе маршрута. Первый аргумент - это строка, содержащая желаемый маршрут /graphql
. Чтобы поддерживать обратную совместимость, мы передаем промежуточное ПО body-parser, которое нам понадобится для любых запросов POST, которые может получить сервер. Затем мы передаем наше persistedQueries
middleware, которое будет вносить изменения в запрос, только если обнаружит хэш в параметрах req.query. Последний параметр - это промежуточное ПО graphqlExpress
от apollo-server-express. Это принимает объект с объединенными результатами нашего makeExecutableSchema
вызова выше.
Также мы создаем маршрут для GraphQL в браузерной IDE GraphQL Playground. Мы будем использовать это для тестирования наших маршрутов POST, чтобы убедиться, что мы не нарушили функциональность сервера GraphQL по умолчанию.
Теперь давайте запустим сервер с помощью следующей команды.
node server.js
Если вы видите сообщение listening on port: 4000
в терминале, значит, все готово. Откройте браузер и перейдите к http://localhost:4000/playground.
. Вы должны увидеть среду IDE graphql-playsky. Скопируйте и вставьте запрос из нашего извлеченного файла запросов в graphql-Playground и запустите запрос, щелкнув значок воспроизведения. Не забудьте добавить переменную name
в раздел переменных внизу, вот так…
Если все сделано правильно, вы должны увидеть результаты своего запроса в виде ответа JSON.
{ "data": { "greeting": { "name": "Ticketmaster", "text": "How are you today?" } } }
Теперь откройте новую вкладку в браузере и перейдите к http://localhost:4000/graphql?hash=1&name=Ticketmaster.
. Вы должны увидеть тот же ответ JSON, что и на предыдущей вкладке, но на этот раз мы используем параметры запроса, а не отправляем на сервер весь документ запроса.
Успех! Теперь мы получаем повышение производительности за счет наших постоянных запросов GraphQL и краевого кэширования из наших URI запросов GET.
Дополнительные материалы для чтения
Мы размахивали руками над множеством разных пакетов, которые делают много крутых вещей. Вот несколько ссылок на несколько почетных упоминаний