Это часть 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, которые может получить сервер. Затем мы передаем наше persistedQueriesmiddleware, которое будет вносить изменения в запрос, только если обнаружит хэш в параметрах 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.

Дополнительные материалы для чтения
Мы размахивали руками над множеством разных пакетов, которые делают много крутых вещей. Вот несколько ссылок на несколько почетных упоминаний
Представляем GraphQL Playground
Мощная IDE GraphQL для улучшения рабочих процессов разработки blog.graph.cool