Мне потребовалось три года, чтобы узнать, как работают 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. Спасибо!