Сегодня очень активно обсуждаются API. А многие даже не могут дать точного определения, что такое API. По сути, API – это интерфейс программирования приложений. Это интерфейс, который позволяет людям (разработчикам, юзерам, потребителям) работать с данными.
Вы можете представить API как бармена. Вы просите у него напиток, а он дает то, что вы попросили. Все просто. Так что же за проблемы возникают?
С начала существования современной сети создание API было не таким уж и трудным. Другое дело – изучение и понимание API. Разработчики формируют большинство людей, которые будут использовать API для создания чего-либо или для потребления данных. Значит, API должен быть как можно более чистым и интуитивным насколько это возможно. Хорошо проработанным API легко пользоваться, его просто изучать. Это тоже следует учитывать при разработке API.
Долгое время для создания API использовался REST. С ним связаны некоторые проблемы. При создании API по дизайну REST вы столкнетесь со следующими трудностями:
- Большое количество конечных точек;
- Разработчикам будет гораздо труднее понимать и изучать ваш API;
- Получение слишком большого или слишком малого количества информации.
Чтобы решить эти проблемы, Facebook создали GraphQL. Мне кажется, сейчас GraphQL – один из лучших способов создания API. Эта статья расскажет вам, почему стоит научиться этому способу уже сейчас.
Из этой статьи вы узнаете принцип работы GraphQL. Я расскажу, как сделать хорошо проработанный, эффективный, функциональный API с помощью GraphQL.
Вы, возможно, уже слышали о GraphQL, так как им пользуются многие люди и компании. Так как GraphQL имеет открытый код, комьюнити сильно разрослось.
Что собой представляет GraphQL?
GraphQL – это свободный язык запросов, который был разработан Facebook. Он предоставляет более действенный способ разработки, создания и использования API. По сути, это замена для REST.
GraphQL имеет большое количество возможностей, например:
Вы пишете, какие данные хотите получить, и получаете именно их. Вы больше не будете получать чрезмерное количество информации, как нередко бывало в REST;
В этом методе единственная конечная точка. Теперь не будет 2, 3 версий одного и того же API;
GraphQL является типизированным языком. Перед исполнением запрос должен быть описан в системе типов. Это способствует разработке более функционального API.
Это было вводное описание GraphQL. Вы узнали, почему это мощный способ создания API и почему он сейчас так популярен.
Начнем разбираться как все устроено в GraphQL
Главная цель этой статьи – не научиться создавать GraphQL сервер, так что мы не будем пока что в это углубляться. Наша цель – познакомиться с методом работы GraphQL на практике. Для этого нами будет использоваться сервер GraphQL без конфигурации – Graphpack.
В начале проекта необходимо создать новую папку. Ее название не важно. У меня она будет называться graphql-server
.
В командной строке напишите:
mkdir graphql-server
На вашем ПК должен быть предустановлен NPM или Yarn. На случай, если вам неизвестно, что это: NPM и Yarn – менеджеры пакетов для языка программирования JavaScript. Для Node.js дефолтным менеджером пакетов служит NPM.
В папке проекта напишите такую команду:
npm init -y
Если же вы пользуетесь Yarn, то команда будет такой:
yarn init
NPM добавит новый файл package.json
. В нем будет находиться каждая зависимость и команда.
Теперь пропишем единственную зависимость, которая нам пригодится в этом проекте.
Graphpack нужен для создания сервера GraphQL с 0 конфигурацией. Так как мы только начинаем работать с GraphQL, это очень сильно поможет нам продвинуться и узнать больше, не тратя время на настройку сервера.
В главной папке наберите в панели управления команду для установки Graphpack:
npm install --save-dev graphpack
Если вы пользуетесь Yarn, то сделайте это так:
yarn add --dev graphpack
Когда Graphpack будет установлен, проверьте скрипты в файле <code rel="CODE">package.json</code>. В него необходимо добавить такой фрагмент кода:
"scripts": {
"dev": "graphpack",
"build": "graphpack build"
}
Мы создадим папку src
и она будет единственной на сервере.
Создайте папку с названием src
. В ней мы добавим лишь 3 файла.
В папке src
необходимо создать файл под названием schema.graphql
. В этом файле добавьте такой код:
type Query {
hello: String
}
В упомянутом файле будет расположена вся схема работы GraphQL. Если вы не знаете, что это, то я объясню это позже. Не волнуйтесь!
Теперь добавим второй файл в папку src
. Пусть его названием будет resolvers.js
. В него необходимо добавить этот код:
import { users } from "./db";
const resolvers = {
Query: {
hello: () => "Hello World!"
}
};
export default resolvers;
Файл resolvers.js
нужен для предоставления инструкций, которые переводят операции GraphQL в данные.
Наконец, создадим в папке src
последний файл. Назовем его db.js
и поместим туда код:
export let users = [
{ id: 1, name: "John Doe", email: "john@gmail.com", age: 22 },
{ id: 2, name: "Jane Doe", email: "jane@gmail.com", age: 23 }
];
В этом туториале мы не будем пользоваться настоящей БД. Файл db.js
будет симулировать БД для обучающих целей.
Теперь папка src
должна выглядеть так:
src
|--db.js
|--resolvers.js
|--schema.graphql
Если вы запустите команду npm run dev
(для Yarn – yarn dev
), то панель управления должна выдать такой ответ:
Теперь можно перейти на localhost:4000
. Это значит, что мы готовы писать первые запросы, мутации, подписки в GraphQL.
Вы увидите GraphQL Playground – мощную интегрированную среду разработки GraphQL, которая улучшает ход разработки.
Схема
В GraphQL есть собственный тип языка, который используется для написания схем. Это удобный для восприятия человеком синтаксис схем, который называется SDL – язык определения схем. SDL остается неизменным вне зависимости от используемой технологии. Его можно применять для всех фреймворков и языков, которые могут вам потребоваться.
Язык схем очень полезен, потому что он позволяет легко понять, какие типы будут использоваться в вашем API.
Типы
Типы – одна из важнейших фич GraphQL. Типы – это пользовательские объекты, которые отображают будущий облик API. Например, если вы создаете приложение-соцсеть, в вашем API будут такие типы: Posts
, Likes
, Groups
, Users
.
У типов есть поля. Поля отвечают конкретным типом данных. Например, рассмотрим тип Users
. В нем должны содержаться поля name
, age
, email
. Типы полей не приципиальны. Они всегда будут возвращать типы данных, такие как: ID
, String
, Float
, Boolean
, Int
, список типов объектов или список типов пользовательских объектов.
Создадим первый тип. Перейдите в файл schema.graphql
и замените тип Query
, который в нем уже находится, на следующий текст:
type User {
id: ID!
name: String!
email: String!
age: Int
}
Каждый пользователь User
будет иметь ID
, так что у него будет тип ID
. У пользователя User
также будет имя name
и электронная почта email
, так что у этих полей будет тип String
. Также будет поле возраста age с типом Int
. Достаточно просто, не так ли?
Что насчет !
в конце каждой строки? Наличие восклицательного знака значит, что поля не могут выдавать значения, являющиеся неопределёнными. Это значит, что все поля без исключения обязаны возвращать данные в ответ на каждый запрос. Неопределенным в типе User
может быть только поле age
.
В GraphQL вы столкнетесь с 3 основными концепциями:
- Запросы (queries) – то, с помощью чего вы будете получать данные с сервера;
- Мутации (mutations) – то, как вы будете модифицировать данные на сервере и возвращать обновленные данные (создавать, обновлять, удалять);
- Подписки (subscriptions) – то, с помощью чего вы будете поддерживать связь с сервером в режиме реального времени.
Все это мы сейчас по порядку рассмотрим и начнем с запросов.
Запросы
Если упрощать, то с помощью запросов в GraphQL вы будете получать данные. Запросы GraphQL прекрасны тем, что вы получите именно необходимые вам данные, не меньше и не больше. Это положительно сказывается на API. Вы больше не будете получать слишком много или слишком мало информации, как бывало с REST API.
Первым типом в GraphQL будет Query
. Последующие запросы будут оказываться в этом типе. Первым делом перейдем в файл schema.graphql
и опишем новый тип Query
:
type Query {
users: [User!]!
}
Все просто: запрос users
будет показывать список из 1 или более пользователей Users. Неопределенные значения возвращаться не будут, так как мы использовали !
. Значит, это определенный запрос. Он обязан постоянно что-либо возвращать.
Можно получить и данные о каком-то конкретном пользователе. Для этого понадобится новый запрос – user
. В типе Query
допишите этот блок кода:
user(id: ID!): User!
После этого он будет выглядеть так:
type Query {
users: [User!]!
user(id: ID!): User!
}
Как вы можете заметить, запросы в GraphQL способны передавать аргументы. В этом случае, чтобы запросить данные определенного пользователя, нужно передать его ID.
Вам может быть интересно: а откуда GraphQL знает, где взять данные? Поэтому-то у нас и есть файл resolvers.js
. Он говорит GraphQL, как и где взять данные.
Сначала необходимо открыть файл resolvers.js
и вставить db.js
, который мы сделали недавно. Файл resolvers.js
будет выглядеть так:
import { users } from "./db";
const resolvers = {
Query: {
hello: () => "Hello World!"
}
};
export default resolvers;
Пора сделать 1 запрос. Откройте файл resolvers.js
, поменяйте замените hello. После этого тип Query станет выглядеть так:
import { users } from "./db";
const resolvers = {
Query: {
user: (parent, { id }, context, info) => {
return users.find(user => user.id === id);
},
users: (parent, args, context, info) => {
return users;
}
}
};
export default resolvers;
Разберемся, как все будет работать.
У каждого распознавателя запросов есть 4 аргумента. Функция user будет передавать id в качестве аргумента, после чего вернет конкретного пользователя, id которого совпадает с переданным. Достаточно просто.
Функция users будет возвращать уже существующий список пользователей.
Теперь посмотрим, хорошо ли работают запросы. Перейдите на localhost:4000
и вставьте следующий код:
query {
users {
id
name
email
age
}
}
Программа должна ответить вам данными всех пользователей.
Если вы хотите получить данные о конкретном пользователе:
query {
user(id: 1) {
id
name
email
age
}
}
Теперь узнаем, что такое мутации. Это один из наиболее важных элементов GraphQL.
Мутации
В GraphQL мутации – это то, как вы собираетесь модифицировать данные, находящиеся на сервере, и получать их обратно с учетом обновлений. По сути, это CUD (Create, Update, Delete – создание, обновление, удаление) в REST.
Мы впервые будем делать тип Mutation в GraphQL. Все мутации, созданные нами, будут оказываться в этом типе. Сначала перейдите в файл schema.graphql
. Пропишите новый тип под названием Mutation
:
type Mutation {
createUser(id: ID!, name: String!, email: String!, age: Int): User!
updateUser(id: ID!, name: String, email: String, age: Int): User!
deleteUser(id: ID!): User!
}
Вы можете понять, что у нас будет всего 3 мутации.
createUser
: мы передаем ID, age, email, name. В ответ мы получим нового пользователя.
updateUser
: мы передаем ID и новые email, age или name. В ответ мы получим нового пользователя.
deleteUser
: мы передаем ID. В ответ мы получим нового пользователя.
Вернемся к файлу resolvers.js
. Под Query добавьте новый объект Mutation:
Mutation: {
createUser: (parent, { id, name, email, age }, context, info) => {
const newUser = { id, name, email, age };
users.push(newUser);
return newUser;
},
updateUser: (parent, { id, name, email, age }, context, info) => {
let newUser = users.find(user => user.id === id);
newUser.name = name;
newUser.email = email;
newUser.age = age;
return newUser;
},
deleteUser: (parent, { id }, context, info) => {
const userIndex = users.findIndex(user => user.id === id);
if (userIndex === -1) throw new Error("User not found.");
const deletedUsers = users.splice(userIndex, 1);
return deletedUsers[0];
}
}
Теперь файл resolvers.js
должен выглядеть следующим образом:
import { users } from "./db";
const resolvers = {
Query: {
user: (parent, { id }, context, info) => {
return users.find(user => user.id === id);
},
users: (parent, args, context, info) => {
return users;
}
},
Mutation: {
createUser: (parent, { id, name, email, age }, context, info) => {
const newUser = { id, name, email, age };
users.push(newUser);
return newUser;
},
updateUser: (parent, { id, name, email, age }, context, info) => {
let newUser = users.find(user => user.id === id);
newUser.name = name;
newUser.email = email;
newUser.age = age;
return newUser;
},
deleteUser: (parent, { id }, context, info) => {
const userIndex = users.findIndex(user => user.id === id);
if (userIndex === -1) throw new Error("User not found.");
const deletedUsers = users.splice(userIndex, 1);
return deletedUsers[0];
}
}
};
export default resolvers;
Проверим, правильно ли работают мутации. Перейдите на localhost:4000
и вставьте следующий код:
mutation {
createUser(id: 3, name: "Robert", email: "robert@gmail.com", age: 21) {
id
name
email
age
}
}
В ответ вы должны получить нового пользователя. Если вы хотите создать новые мутации, то попробуйте сделать это самостоятельно! Можно удалить юзера, которого вы же и добавили, чтобы понять, все ли работает.
Наконец, ознакомимся с подписками и узнаем, почему они так важны.
Подписки
Как говорилось ранее, подписки – это то, с помощью чего вы собираетесь поддерживать связь с сервером в режиме реального времени. Значит, когда на сервере будет осуществляться какое-либо событие или когда его будут запрашивать, сервер отправит необходимые данные клиенту.
Работая с подписками, вы можете своевременно обновлять приложение для разных пользователей.
Простая подписка выглядит так:
subscription {
users {
id
name
email
age
}
}
Можно заметить очевидное сходство с запросами. Это действительно так. Работает же она иначе.
Когда на сервере происходит какое-либо обновление, он запустит запрос GraphQL, прописанный в подписке, и отправит результат, в котором учтено обновление, клиенту.
В представленной статье не будут подробно разбираться подписки.
Заключение
Как вы могли заметить, GraphQL – новая технология с широкими возможностями. Она позволяет создавать замечательно проработанные, отличные API. Поэтому я рекомендую начать изучать ее уже сейчас. Для меня она постепенно заменит REST.