GraphQL — это язык запросов к API-интерфейсам. Он отображает предоставляемые сервером данные, чтобы клиент смог выбрать именно то, что ему нужно.
Помимо этого, с помощью GraphQL можно получить несколько ресурсов сервера одним вызовом, а не выполнять множество вызовов REST API.
Однако трудно оценить преимущества GraphQL, не попробовав его в действии. Поэтому рассмотрим использование GraphQL на практических примерах.
Мы будем использовать GraphQL вместе с NodeJS.
Предварительные требования
Перед началом работы установите NodeJS.
Как использовать GraphQL с NodeJs
GraphQL используется с различными ЯП. Рассмотрим пример использования GraphQL с JavaScript, используя NodeJS.
Создайте папку под названием graphql-with-nodejs. Запустите команду npm init в папке проекта, чтобы создать проект NodeJS. Команда представлена ниже:
cd graphql-with-nodejs npm init
Установка зависимостей
Установите Express с помощью следующей команды:
npm install express
Установите GraphQL с помощью следующей команды. Устанавливаем GraphQL и GraphQL для Express.
npm install express-graphql graphql
Код NodeJS
Создайте файл server.js внутри проекта и скопируйте в него следующий код:
const express = require('express'); const port = 5000; const app = express(); app.get('/hello', (req,res) => { res.send("hello"); } ); app.listen(port); console.log(`Server Running at localhost:${port}`);
Приведенный код содержит одну конечную точку (endpoint) HTTP GET с названием /hello.
Endpoint создается с помощью Express.
Теперь изменим этот код, чтобы запустить GraphQL.
Запуск GraphQL в коде
У GraphQL будет один URL endpoint с именем /graphql, который обрабатывает все запросы.
Скопируйте следующий код в server.js:
//get all the libraries needed const express = require('express'); const graphqlHTTP = require('express-graphql'); const {GraphQLSchema} = require('graphql'); const {queryType} = require('./query.js'); //setting up the port number and express app const port = 5000; const app = express(); // Define the Schema const schema = new GraphQLSchema({ query: queryType }); //Setup the nodejs GraphQL server app.use('/graphql', graphqlHTTP({ schema: schema, graphiql: true, })); app.listen(port); console.log(`GraphQL Server Running at localhost:${port}`);
Теперь пройдемся по коду.
С помощью graphqlHTTP сервер GraphQL устанавливается в URL-адрес /graphql. Он обрабатывает поступающие запросы.
Процесс установки представлен в следующих строчках кода:
app.use('/graphql', graphqlHTTP({ schema: schema, graphiql: true, }));
Теперь изучим параметры внутри graphqlHTTP.
graphiql
graphiql — это веб-интерфейс, с помощью которого тестируются endpoints GraphQL. Устанавливаем значение true, чтобы было легче тестировать различные endpoints GraphQL.
schema
У GraphQL есть лишь один endpoint /graphql. Он может обладать несколькими endpoints, выполняющими различные действия. Они указаны в схеме.
Схема выполняет следующие действия:
- Указывает различные endpoints
- Определяет поля ввода и вывода для endpoint
- Определяет действие, которое должно быть выполнено при достижении endpoint и так далее.
Схема определяется в коде следующим образом:
const schema = new GraphQLSchema({ query: queryType });
Схема может содержать как запрос, так и типы мутации. Мы будем рассматривать только тип query.
query
По схеме видно, что query установлен в queryType.
Импортируем queryType из файла query.js с помощью следующей команды:
const {queryType} = require('./query.js');
query.js — это пользовательский файл.
query — это место, куда мы определяем endpoints типа read-only в схеме.
Создайте файл с названием query.js в проекте и скопируйте в него следующий код.
const { GraphQLObjectType, GraphQLString } = require('graphql'); //Define the Query const queryType = new GraphQLObjectType({ name: 'Query', fields: { hello: { type: GraphQLString, resolve: function () { return "Hello World"; } } } }); exports.queryType = queryType;
query Explained
queryType создан как GraphQLObjectType с названием Query.
fields — это место, куда мы определяем endpoints.
Здесь мы добавляем один endpoint под названием hello.
hello относится к типу GraphQLString, т.е возвращает тип String. В схеме GraphQL используется тип GraphQLString вместо String. Прямое использованиеString не работает.
Функция resolve указывает действие, которое должно быть выполнено при достижении endpoint. В данном случае возвращается строка “Hello World”.
Теперь экспортируем querytype с помощью exports.queryType = queryType, чтобы убедиться, что он может быть импортирован в server.js.
Запуск приложения
Запустите приложение, используя следующую команду:
node server.js
Приложение работает на localhost:5000/graphql.
Можно протестировать приложение, перейдя на localhost:5000/graphql.
Этот URL-адрес управляет веб-интерфейсом Graphiql, как указано на скриншоте ниже.
Входные данные указаны слева, а выходные — справа.
Вводим следующее:
{ hello }
В результате получаем:
{ "data": { "hello": "Hello World" } }
Поздравляем😃
Вы создали свой первый endpoint GraphQL.
Добавление нескольких endpoints
Создадим еще две конечных точки (endpoints):
- movie: этот endpoint возвращает фильм, получивший ID фильма
- director: этот endpoint возвращает режиссера, получившего ID режиссера. Он также вернет все фильмы, снятые этим режиссером.
Добавление данных
Обычно приложение прочитывает данные из Базы данных. Но в этом примере, мы будем хардкодить данные в самом коде.
Создайте файл data.js и скопируйте в него следующий код.
//Hardcode some data for movies and directors let movies = [{ id: 1, name: "Movie 1", year: 2018, directorId: 1 }, { id: 2, name: "Movie 2", year: 2017, directorId: 1 }, { id: 3, name: "Movie 3", year: 2016, directorId: 3 } ]; let directors = [{ id: 1, name: "Director 1", age: 20 }, { id: 2, name: "Director 2", age: 30 }, { id: 3, name: "Director 3", age: 40 } ]; exports.movies = movies; exports.directors = directors;
В этом файле содержатся данные о фильмах и режиссерах. Используем данные из этих файлов для наших endpoints.
Добавление endpoint movie в запрос
Новые endpoints будут добавлены к queryType в файл query.js.
Код для endpoint movie представлен ниже:
movie: { type: movieType, args: { id: { type: GraphQLInt } }, resolve: function (source, args) { return _.find(movies, { id: args.id }); } }
Тип возвращаемого значения этого endpoint — movieType, который скоро будет определен.
С помощью параметра args указываются входные данные для endpoint movie. Входные данные для этого endpoint — id типа GraphQLInt.
Функция resolve возвращает фильм, соответствующий id, из списка фильмов. Функция find из библиотеки lodash используется для поиска элемента в списке.
Полный код для query.js представлен ниже:
const { GraphQLObjectType, GraphQLString, GraphQLInt } = require('graphql'); const _ = require('lodash'); const {movieType} = require('./types.js'); let {movies} = require('./data.js'); //Define the Query const queryType = new GraphQLObjectType({ name: 'Query', fields: { hello: { type: GraphQLString, resolve: function () { return "Hello World"; } }, movie: { type: movieType, args: { id: { type: GraphQLInt } }, resolve: function (source, args) { return _.find(movies, { id: args.id }); } } } }); exports.queryType = queryType;
Из приведенного кода видно, что movieType действительно определен в types.js.
Добавление пользовательского типа movieType
Создайте файл с названием types.js.
Добавьте следующий код в types.js:
const { GraphQLObjectType, GraphQLID, GraphQLString, GraphQLInt } = require('graphql'); // Define Movie Type movieType = new GraphQLObjectType({ name: 'Movie', fields: { id: { type: GraphQLID }, name: { type: GraphQLString }, year: { type: GraphQLInt }, directorId: { type: GraphQLID } } }); exports.movieType = movieType;
movieType создан в качестве GraphQLObjectType.
Он обладает четырьмя полями: id, name, year и directorId. Типы для каждого поля устанавливаются в процессе их добавления.
Эти поля поступают непосредственно из данных. В данном случае, из списка movies.
Добавление запроса и типа для endpoint director
В query.js endpoint director добавляется следующим образом:
director: { type: directorType, args: { id: { type: GraphQLInt } }, resolve: function (source, args) { return _.find(directors, { id: args.id }); } }
directorType добавляется следующим образом в types.js:
//Define Director Type directorType = new GraphQLObjectType({ name: 'Director', fields: { id: { type: GraphQLID }, name: { type: GraphQLString }, age: { type: GraphQLInt }, movies: { type: new GraphQLList(movieType), resolve(source, args) { return _.filter(movies, { directorId: source.id }); } } } });
Минуточку. directorType немного отличается от movieType. В чем причина?
Почему функция resolve находится внутри directorType? Ранее функции resolve присутствовали только в query…
Особенности directorType
При вызове endpoint director необходимо возвратить подробную информацию о режиссере, а также всего его фильмы.
Первые три поля id, name, age в directorType исходят непосредственно из данных (список directors).
Четвертое поле movies должно содержать список фильмов этого режиссера.
По этой причине, следует упомянуть, что тип поля movies относится к GraphQLList от movieType (Список фильмов).
Однако каким образом найти все фильмы, снятие этим режиссером?
Для этого в поле movies есть функция resolve. Входные данные для функции resolve — source и args.
source будет содержать подробную информацию о родительском объекте.
Допустим, поля для режиссера id =1, name = “Random” и age = 20. Затем source.id =1, source.name = “Random” и source.age = 20
Таким образом, в этом примере функция resolve найдет все фильмы, в которых directorId соответствует Id запрашиваемого режиссера.
Тестирование приложения
Протестируем приложение для различных сценариев.
Запустите приложение с помощью node server.js.
Зайдите на localhost:5000/graphql и введите следующие данные.
movie
Входные данные:
{ movie(id: 1) { name } }
Результат:
{ "data": { "movie": { "name": "Movie 1" } } }
Приведенный пример показывает, что клиент может запросить именно то, что ему нужно, а GraphQL предоставит необходимые параметры. Сервер выдает только запрошенное поле name.
В movie(id: 1) id находится во входном параметре. Мы запросили у сервера фильм с id=1.
Входные данные:
{ movie(id: 3) { name id year } }
Выход:
{ "data": { "movie": { "name": "Movie 3", "id": "3", "year": 2016 } } }
В приведенном выше примерe сервер выдает запрашиваемые поля name, id и year.
director
Входные данные:
{ director(id: 1) { name id, age } }
Результат:
{ "data": { "director": { "name": "Director 1", "id": "1", "age": 20 } } }
Входные данные:
{ director(id: 1) { name id, age, movies{ name, year } } }
Получаем:
{ "data": { "director": { "name": "Director 1", "id": "1", "age": 20, "movies": [ { "name": "Movie 1", "year": 2018 }, { "name": "Movie 2", "year": 2017 } ] } } }
Приведенный пример показывает возможности GraphQL. Нам нужен режиссер с id=1 и все его фильмы. Поля director и movie являются произвольными, поэтому клиент может запросить именно то, что ему нужно.
Аналогичным образом можно запрашивать другие поля и типы. К примеру, можно сделать запрос: Поиск режиссера с id=1 и всех его фильмов. Поиск актеров для каждого фильма, а для каждого актера 5 его лучших фильмов и так далее. Для этого запроса необходимо указать отношения между типами и тогда можно будет запросить любое необходимое отношение.
Поздравляем 😃
Теперь вы знакомы с базовыми концепциями GraphQL.
Чтобы узнать больше, можете посмотреть официальную документацию.
Читайте также
Специально для сайта ITWORLD.UZ. Новость взята с сайта NOP::Nuances of programming