Доброго времени суток, друзья!
Создание GraphQL сервера еще никогда не было таким простым, спасибо Express и Node.js.
Помните старый добрый протокол SOAP? Возможно, уже забыли, поскольку его заменил REST, и он намного лучше. Но что если я скажу вам, что скоро мы также забудем REST из-за GraphQL?
Необходимость получения данных
GraphQL — это язык запросов, созданный Facebook в 2012 и открытый (имеется ввиду ставший «опенсорным») в 2015. Он предназначен для получения и работы с определенными данными, предоставляя клиенту больше контроля за тем, какая информация отправляется на сервер. Одной из главных проблем REST является перегруженность данных: допустим, мы хотим получить имя и email пользователя, а вместо этого получаем весь профиль… зачем нам так много лишней информации? Теперь представьте, что нам нужен только список ID пользователей, однако API возвращает огромный массив объектов — профилей пользователей. Какое нерациональное расходование времени и ресурсов! GraphQL предоставляет API избирательного (выборочного) получения данных, который позволяет получать только то, что нужно.
Типобезопасность
GraphQL написан на SDL (Schema Definition Language), что обуславливает его строгую типизацию. Схемы GraphQL валидируются (проверяются) во время компиляции, т.е. компиляция завершится неудачно при обнаружении любой ошибки в типах данных.
Признание устаревшим и версионирование
Возвращаясь к REST, следует помнить, что со временем некоторые API могут быть признаны устаревшими. Может потребоваться переключиться с «someapi.dev/v1/myendpoint» на «someapi.dev/v2/myNewEndpoint» из-за изменения структуры ресурсов. GraphQL позволяет избежать этого, поскольку клиент запрашивает API по аналогии с базой данных. Структура сервера может измениться, но клиент по-прежнему будет иметь возможность получить содержимое в ожидаемом формате.
Создание сервера
Хорошо, давайте создадим сервер. Создаем проект и устанавливаем следующие пакеты:
$ yarn add graphql express express-graphql
Отлично. Создаем файл "./main.js" и определяем новый Express Server:
const express = require('express') const app = express() const port = process.env.PORT || 3000 app.get('/' (req, res) => { res.send('Hello World!') }) app.listen(port, () => { console.log(`Server ready on port http://localhost:${port}`) })
Запустим сервер и посмотрим, как он работает: npm ./main.js

Страница выглядит не лучшим образом, но сервер работает! Добавляем конечную точку GraphQL, используя замечательный инструмент GraphiQL:
const express = require('express') const expressGraphQL = require('express-graphql') const { buildSchema } = require('graphql') const app = express() const port = process.env.PORT || 3000 const schema = buildSchema(` type Query { message: String } `) const rootValue = { message() { return 'Hello World!' } app.use('/graphql', expressGraphQL({ schema, rootValue, graphiql: true })) app.get('/', (req, res) => { res.send('Hello World!') }) app.listen(port, () => { console.log(`Server ready on port http://localhost:${port}`) }) }
Вот что мы здесь делаем:
- Импортируем промежуточный слой Express GraphQL (express-graphql)
- Импортируем функцию «buildSchema», позволяющую строить схему (Schema) GraphQL, которая описывает данные из источника
- На 7 строчке строим схему, используя SDL. При этом необходимо определить тип каждого значения (в нашем случае, значения message, которое является строкой)
- На 13 строчке определяем Resolver: он содержит ссылки операций на функции. Когда вы запрашиваете данные message, Resolver возвращает строку «Hello World!». Легко, не правда ли?
- на 19 строчке создаем конечную точку "/graphql", которая также использует express-graphql для создания интерфейса GraphiQL
Перезапустим сервер и перейдем по адресу "http://localhost:3000/graphql":

Круто! Мы создали конечную точку GraphQL с помощью нескольких строчек кода! Протестируем ее, запросив message:

Превосходно. Мы только что создали наш первый GraphQL сервер! Создадим новый файл "./data.js":
module.exports = [{ id: 1, name: "Java", createdBy: "James Gosling", year: 1995, paradigm: "Object Oriented", complexity: 7 }, { id: 2, name: "PHP", createdBy: "Rasmus Lerdorf", year: 1995, paradigm: "Object Oriented", complexity: 3 }, { id: 3, name: "C", createdBy: "Dennis Ritchie", year: 1972, paradigm: "Imperative", complexity: 10 }, { id: 4, name: "Erlang", createdBy: "Joe Armstrong", year: 1986, paradigm: "Functional", complexity: 8 }, { id: 5, name: "OCaml", createdBy: "INRIA", year: 1996, paradigm: "Functional", complexity: 8 }, { id: 6, name: "LISP", createdBy: "John McCarthy", year: 1958, paradigm: "Functional", complexity: 6 } ]
Хорошо, теперь у нас имеется небольшой набор данных. С помощью сервера мы можем получать определенную информацию из этого набора.
Для улучшения нашего кода создадим третий файл "./graphqlData.js":
const { buildSchema } = require('graphql') const data = require('./data') const schema = buildSchema(` type Query { language(id: Int!): Language languages(paradigm: String): [Language] } type Language { id: Int name: String createBy: String year: Int paradigm: String complexity: Int } `)
Импортируем наши данные и buildSchema. Далее необходимо определить схему конечной точки GraphQL. Как видите, прежде всего мы определяем тип Query, что позволяет нам:
- Получать определенный язык по ID (который является целым числом). Возвращается Language, о типе которого поговорим позже
- Получать список (массив в JavaScript) языков, имеющих определенную парадигму. Типом парадигмы является строка (String), возвращается список (массив) языков (Language).
В конце мы определяем тип Language. При определении нового GraphQL типа необходимо описать запрашиваемые данные. У нас имеется ID с типом Int, название с типом String и т.д.
Двигаемся дальше. Создадим две функции, отвечающие за получение данных:
function getLanguage(args) { return data.filter(language => language.id === args.id)[0] } function getLanguages(args) { return args.paradigm ? data.filter(language => language.paradigm === args.paradigm) : data } const rootValue = { language: getLanguage, languages: getLanguages }
Вот что делают эти функции и константа:
- getLanguage принимает Object как аргумент и фильтрует данные, возвращая язык с определенным ID
- getLanguages также принимает Object как аргумент и возвращает языки определенной парадигмы
- rootValue готовит данные к использованию в express-graphql
Помните, что обе функции возвращают одинаковый тип, который мы определили в схеме.
Объединяем код:
const { buildSchema } = require("graphql"); const data = require("./data"); const schema = buildSchema(` type Query { language(id: Int!): Language languages(paradigm: String): [Language] } type Language { id: Int name: String createdBy: String year: Int paradigm: String complexity: Int } `); function getLanguage(args) { return data.filter(language => language.id === args.id)[0] } function getLanguages(args) { return args.paradigm ? data.filter(language => language.paradigm === args.paradigm) : data } const rootValue = { language: getLanguage, languages: getLanguages, } module.exports = { schema, getLanguage, getLanguages, rootValue }
Отлично. Немного изменим main.js:
const express = require('express') const expressGraphQL = require('express-graphql') const graphQLData = require('./graphQLData') const app = express() const port = process.env.PORT || 3000 app.use('/graphql', expressGraphQL({ schema: graphQLData.schema, rootValue: graphQLData.rootValue, graphiql: true })) app.get('/', (req, res) => { res.send('Hello World!') }) app.listen(port, () => { console.log(`Server ready on port http://localhost:${port}`) })
Тестируем сервер:

Круто. Что если удалить некоторые значения из запроса?

Легко и просто. Что если мы хотим получить определенный язык?

Мы сделали это. Мы создали простой GraphQL сервер. При этом данные можно получать из базы данных, из файла, откуда угодно. Представьте, какие возможности перед нами открываются!
Благодарю за внимание.
ссылка на оригинал статьи https://habr.com/ru/post/499084/
Добавить комментарий