Мне потребовалось три года, чтобы узнать, как работают RESTful API и как их создавать, - а это долгое время, чтобы чему-то научиться. Поэтому, когда я впервые прочитал о GraphQL, я почувствовал, как мир рушится вокруг меня.

«технология, которая готова заменить или, по крайней мере, радикально изменить способ разработки и представления API - GraphQL».

Поначалу это было неприятно. Я не хотел выбирать другой способ создания API, потому что мне потребовалась целая вечность, чтобы понять, как создавать их с помощью REST. Но недавно я все равно заставил себя создать что-нибудь с GraphQL и сделал демонстрационное приложение, используя Express и Apollo. Проработав над этим неделю, я понял, что использование GraphQL значительно упрощает разработку API, особенно если вы ожидаете, что ваше приложение вырастет в размерах.

Вот почему так проще.

Здесь у нас есть файл сервера с маршрутами на основе REST.

const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const mongoose = require('mongoose');
const Guests = require('../models/Guests');
const app = express();
const port = 8080;
app.use(bodyParser.json());
app.use(cors());
app.get('/guests', async (req, res) => {
 const guests = await Guests.find({}, (err, data) => data);
 res.json(guests);
});
app.post('/guest', async (req, res) => {
  const guest = await Guests.create(res);
  return guest;
});
app.put('/guest/:id', async (req, res) => {
 const updatedGuest =  await Guests.update({ _id: req.params.id }, {name: req.body.name, contact: req.body.contact});
 return updatedGuest;
});
app.delete('/guest/:id', async (req, res) => {
 const deletedGuest = await Guests.findByIdAndRemove(req.params.id);
  return deletedGuests;
});
app.listen(port, () => {
  console.log(`Server running on: ${port}`);
});

Ничего особенного, но если бы это приложение увеличивалось, этот файл начал бы выглядеть беспорядочно из-за новых маршрутов. Проблема с маршрутами в том, что они представляют собой разновидность одного и того же фрагмента кода. Вы даже можете заявить, что это СУХИЙ код, потому что вы повторяете один и тот же код снова и снова.

GraphQL не предназначен для построения маршрутов. GraphQL разработан для оптимизации всего вашего API с помощью единого маршрута. Вы фокусируетесь не на сопоставлении данных с URL-адресами, а на переводе запросов и мутаций данных в синтаксис GraphQL.

Давайте перестроим REST API, описанный выше, с помощью сервера GraphQL с помощью Apollo & Express.

Это базовый каркас сервера GraphQL.

const express = require('express');
const bodyParser = require('body-parser');
const { makeExecutableSchema } = require('graphql-tools');
const cors = require('cors');
const Guests = require('../models/Guests');
const { graphqlExpress } = require('apollo-server-express');
const app = express();
const port = 8080;
const typeDefs = `
  
`;
const resolvers = {
  Query: {
    
  },
  Mutation: {
   
  },
};
const schema = makeExecutableSchema({ typeDefs, resolvers });
app.use(cors());
app.use('/guests', cors(), bodyParser.json(), graphqlExpress({ schema }));
app.listen(port, () => {
  console.log(`Server running on: ${port}`);
})

Сильно отличается от файла, который мы видели раньше. Ключевые отличия заключаются в том, что у нас есть один маршрут, две новые функции и три новые переменные.

Давайте пройдемся по этому файлу сверху вниз, чтобы охватить все. Две новые функции: makeExecutableSchema и graphqlExpress.

const { makeExecutableSchema } = require('graphql-tools');
const { graphqlExpress } = require('apollo-server-express');

Функция makeExecutableSchema из библиотеки graphql-tools имеет единственную цель. Эта функция принимает схему и связанные запросы и изменения как один аргумент и преобразует их в исполняемый код javascript.

Примечание: основная часть настраиваемого кода, написанного разработчиком приложения GraphQL, вращается вокруг построения этой схемы и связанных запросов и изменений.

Вторая функция, функция graphqlExpress из apollo-server-express, также имеет единственную цель. Его цель - подключить этот новый код javascript к маршруту.

app.use('/guests', cors(), bodyParser.json(), graphqlExpress({ schema }));

Примечание. Я добавил в маршрут промежуточное ПО bodyParser.json () и cors (), чтобы разрешить публикацию и предотвратить проблемы с совместным использованием ресурсов между источниками.

Это все, что нужно для настройки. Теперь давайте сосредоточимся на создании кода, специфичного для этого CRUD-приложения.

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

const typeDefs = `
  type Query { guest: [Guest]},
  type Mutation { addGuest( name: String, contact: String): Guest,  updateGuest (_id: String) : Guest, removeGuest (_id:String) : Guest } 
  type Guest { _id: String, name: String, contact: String}
`;

Выше у нас есть переменная typeDefs, которая соответствует схеме, полностью написанной на GraphQL. Вот разбивка:

type Query {guest: [Guest]} - это способ GraphQL сказать, что мы хотим написать запрос с именем guest, который будет получать все документы из коллекции Guest, когда выполнен.

Это будет выглядеть примерно так.

query {
  guest
}

t ype Mutation {addGuest (name: String, contact: String): Guest, updateGuest (_id: String): Guest, removeGuest (_id: String): Guest} - Это схема GraphQL для списка функций CRUD, используемых для изменения документов в гостевой коллекции.

type Guest {_id: String, name: String, contact: String} - мы выполняем все эти запросы и изменения коллекции Guest, но GraphQL не понимает форму документов внутри этой коллекции. Итак, нам нужно написать схему для типа данных, хранящихся в гостевой коллекции.

Второй аргумент для makeExecutableSchema называется преобразователи, и его цель - оживить схему typeDefs. преобразователи - это объект, содержащий методы, которые коррелируют с запросами и изменениями, определенными в схеме GraphQL. Эти методы будут напрямую взаимодействовать с нашей гостевой моделью и выполнять все операции CRUD.

const resolvers = {
  Query: {
    guests: () => Guests.find({}, (err, data) => data),
  },
  Mutation: {
    addGuest: async (root, args) => {
      const guest = await Guests.create(args);
      return Guests.findOne({ _id: guest._id });
    },
    updateGuest: async (root, args) => {
      const id = await JSON.parse(args._id);
      return Guests.update({ _id: id }, {name: args.name, contact: args.contact}, (data => data));
    },
    deleteGuest: async (root, args) => {
       const deletedGuest = await Guests.findByIdAndRemove(req.params.id);
  return deletedGuests;
    },
  },
};

Выглядит знакомо? Здесь мы объединяем термины, определенные в схеме GraphQL, и методы мангуста внутри одного объекта, организуя их либо в ключ запроса, либо в ключ мутаций.

Это все, что нам нужно сделать, чтобы наш GraphQL работал внутри приложения JavaScript. По завершении мы объединяем их, передавая их как один объект в функцию makeExectuableSchema и устанавливая для этой функции переменную с именем schema.

const schema = makeExecutableSchema({ typeDefs, resolvers });

Теперь мы подключаем переменную схему к единственному экспресс-маршруту, выполнив следующие действия:

app.use('/guests', cors(), bodyParser.json(), graphqlExpress({ schema }));

И это все! Это все, что нужно для преобразования RESTful API в GraphQL API.

В заключение скажем, что вы хотите масштабировать этот API. Все, что вам нужно сделать сейчас, это обновить схему typeDefs с помощью модели данных или метода в GraphQL. А затем обновите объект преобразователей с помощью соответствующего метода изменения или запроса.

Больше не нужно беспокоиться о новых URL-адресах, больше не нужно беспокоиться о попытках вспомнить, какой маршрут соответствует каким данным. Когда ваше приложение растет, вы обновляете переменную typeDefs и объект resolvers, а затем забываете об этом. Вы не можете сделать это намного проще. Я надеюсь, что, прочитав эту статью, вы почувствуете меньше опасений и рассмотрите возможность попробовать graphQL в своем следующем проекте.

Спасибо за чтение!

P.S. Если вы хотите возиться с приложением с взаимодействующим сервером graphql и реагирующим клиентом, не стесняйтесь форкнуть мое репо https://github.com/ptums/simple-graphql-express-react-demo. И если вы обнаружите ошибки или что-то не работает, напишите мне или создайте проблему с git. Спасибо!