GraphQL - это язык запросов для API. Он показывает, какие типы данных предоставляет сервер, а затем клиент может выбрать именно то, что ему нужно.

Также в GraphQL вы можете получить несколько ресурсов сервера за один вызов, а не выполнять несколько вызовов REST API.

Вы можете ознакомиться с полным списком преимуществ на https://graphql.org/.

Дело в том, что пока вы не увидите GraphQL в действии, трудно понять преимущества. Итак, приступим к использованию GraphQL.

В этой статье мы будем использовать GraphQL вместе с NodeJS.

Предварительные условия

Установите NodeJS отсюда: https://nodejs.org/en/.

Как использовать 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}`);

В приведенном выше коде есть одна конечная точка HTTP GET с именем / hello.

Конечная точка создается с помощью Express.

Теперь давайте изменим этот код, чтобы включить GraphQL.

Включение GraphQL в коде

GraphQL будет иметь единственную конечную точку URL с именем / 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 - это веб-интерфейс, с помощью которого вы можете тестировать конечные точки GraphQL. Мы установим для него значение true, чтобы было легче тестировать различные конечные точки GraphQL, которые мы создаем.

схема

GraphQL имеет только одну внешнюю конечную точку / graphql. Эта конечная точка может иметь несколько других конечных точек, выполняющих различные действия. Эти конечные точки будут указаны в схеме.

Схема будет делать такие вещи, как:

  • Укажите конечные точки
  • Укажите поля ввода и вывода для конечной точки
  • Укажите, какое действие следует выполнить при достижении конечной точки и т. Д.

Схема определяется в коде следующим образом:

const schema = new GraphQLSchema({ query: queryType });

Схема может содержать типы запроса, а также мутации. В этой статье речь пойдет только о типе запроса.

запрос

На схеме видно, что для запроса задано значение queryType.

Мы импортируем queryType из файла query.js с помощью следующей команды:

const {queryType} = require('./query.js');

query.js - это настраиваемый файл, который мы скоро создадим.

запрос - это место, где мы указываем в схеме конечные точки, доступные только для чтения.

Создайте в проекте файл с именем 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;

Разъяснение запроса

queryType создается как GraphQLObjectType и получает имя Query.

поля - это то место, где мы указываем различные конечные точки.

Итак, здесь мы добавляем одну конечную точку с именем hello.

hello имеет тип GraphQLString, что означает, что эта конечная точка имеет тип возвращаемого значения String. Тип - GraphQLString вместо String, поскольку это схема GraphQL. Таким образом, прямое использование String не будет работать.

Функция resolve указывает действие, которое должно быть выполнено при вызове конечной точки. Здесь действие состоит в том, чтобы вернуть строку «Hello World».

Наконец, мы экспортируем тип запроса, используя exports.queryType = queryType. Это необходимо для того, чтобы мы могли импортировать его в server.js.

Запуск приложения

Запустите приложение, используя следующую команду:

node server.js

Приложение работает на localhost: 5000 / graphql.

Вы можете протестировать приложение, перейдя по адресу localhost: 5000 / graphql.

Этот URL-адрес запускает веб-интерфейс Graphiql, как показано на экране ниже.

Слева указаны входные данные, а справа - выходные данные.

Введите следующие данные

{
  hello
}

Это даст следующий результат

{
  "data": {
    "hello": "Hello World"
  }
}

Поздравляю 😃

Вы создали свою первую конечную точку GraphQL.

Добавление дополнительных конечных точек

Мы создадим 2 новых конечных точки:

  • фильм: эта конечная точка вернет фильм с учетом идентификатора фильма.
  • директор: эта конечная точка вернет директора с идентификатором директора. Он также вернет все фильмы режиссера.

Добавление данных

Обычно приложение считывает данные из базы данных. Но в этом руководстве мы будем жестко кодировать данные в самом коде для простоты.

Создайте файл с именем 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;

В этом файле содержатся данные о фильмах и режиссерах. Мы будем использовать данные из этого файла для наших конечных точек.

Добавление конечной точки фильма в запрос

Новые конечные точки будут добавлены в queryType в файле query.js.

Код для конечной точки фильма показан ниже:

movie: {
            type: movieType,
            args: {
                id: { type: GraphQLInt }
            },
            resolve: function (source, args) {
                return _.find(movies, { id: args.id });
            }
        }

Тип возврата этой конечной точки - movieType, который будет определен в ближайшее время.

Параметр args используется для указания входа в конечную точку фильма. Входными данными для этой конечной точки является id типа GraphQLInt.

Функция resolve возвращает фильм, соответствующий идентификатору, из списка фильмов. 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.

В нем 4 поля: id, name, year и directorId. Типы для каждого из этих полей также указываются при их добавлении.

Эти поля берутся непосредственно из данных. В этом случае это будет из списка фильмов.

Добавление запроса и типа для конечной точки директора

Как и в случае с фильмом, может быть добавлена ​​даже конечная точка режиссера.

В query.js конечную точку директора можно добавить следующим образом:

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. Почему это?

Почему внутри DirectorType есть функция разрешения? Ранее мы видели, что функции разрешения присутствовали только в запросе…

Особенность DirectorType

Когда вызывается конечная точка режиссер, мы должны вернуть сведения о режиссере, а также обо всех фильмах, которые режиссер поставил.

Первые 3 поля id, name, age в DirectorType просты и берутся непосредственно из данных (список директоров).

Четвертое поле фильмы должно содержать список фильмов этого режиссера.

Для этого мы упоминаем, что тип поля фильмы - это GraphQLList типа movieType (список фильмов).

Но как именно найти все фильмы этого режиссера?

Для этого у нас есть функция разрешения внутри поля фильмов. Входными данными для этой функции разрешения являются источник и аргументы.

source будет содержать сведения о родительском объекте.

Допустим, у директора есть поля id = 1, name = «Random» и age = 20. Затем source.id = 1, source.name = «Random» и source.age = 20.

Итак, в этом примере функция resolve находит все фильмы, в которых DirectorId совпадает с идентификатором требуемого Director.

Код

Полный код этого приложения доступен в этом репозитории GitHub.

Тестирование приложения

Теперь давайте протестируем приложение для разных сценариев.

Запустите приложение, используя node server.js.

Перейдите к localhost: 5000 / graphql и попробуйте ввести следующие данные.

кино

Вход:

{
  movie(id: 1) {
    name
  }
}

Выход:

{
  "data": {
    "movie": {
      "name": "Movie 1"
    }
  }
}

Из вышесказанного мы видим, что клиент может запрашивать именно то, что он хочет, а GraphQL гарантирует, что обратно будут отправлены только эти параметры. Здесь запрашивается только поле name, и только оно отправляется сервером.

В movie(id: 1) id - это входной параметр. Мы просим сервер отправить обратно фильм с идентификатором 1.

Вход:

{
  movie(id: 3) {
    name
    id
    year
  }
}

Выход:

{
  "data": {
    "movie": {
      "name": "Movie 3",
      "id": "3",
      "year": 2016
    }
  }
}

В приведенном выше примере запрашиваются поля имя, идентификатор и год. Таким образом, сервер отправляет обратно все эти поля.

директор

Вход:

{
  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. Мы указываем, что нам нужен режиссер с идентификатором 1. Кроме того, мы указываем, что нам нужны все фильмы этого режиссера. Поля «Режиссер» и «Фильм» настраиваются, и клиент может запросить именно то, что ему нужно.

Точно так же это можно распространить на другие поля и типы. Например, мы могли бы выполнить запрос типа Найти режиссера с идентификатором 1. Для этого режиссера найти все фильмы. Для каждого фильма найдите актеров. Для каждого актера получают 5 лучших фильмов и так далее. Для этого запроса нам нужно указать отношения между типами. Как только мы это сделаем, клиент может запросить любые отношения, которые он хочет.

Поздравляю 😃

Теперь вы знаете основные концепции GraphQL.

Вы можете ознакомиться с документацией, чтобы узнать больше о GraphQL.

Об авторе

Я люблю технологии и слежу за достижениями в этой области. Мне также нравится помогать другим своими знаниями в области технологий.

Не стесняйтесь связаться со мной в моей учетной записи LinkedIn https://www.linkedin.com/in/aditya1811/

Вы также можете подписаться на меня в твиттере https://twitter.com/adityasridhar18

Мой сайт: https://adityasridhar.com/

Первоначально опубликовано на adityasridhar.com.