GraphQL API с JavaScript: пошаговый туториал
GraphQL — это язык запросов для API, который позволяет клиентам запрашивать только те данные, которые им действительно нужны. В отличие от REST, где структура ответа фиксирована, GraphQL дает гибкость и контроль. В этом туториале мы создадим простой GraphQL API на Node.js с использованием популярной библиотеки Apollo Server.
Вы научитесь настраивать сервер, определять типы данных, писать резолверы и выполнять запросы и мутации. Все примеры кода можно запустить локально.
1. Настройка окружения и установка зависимостей
Прежде чем писать код, убедитесь, что у вас установлен Node.js (версия 14 или выше). Создайте новую папку для проекта и инициализируйте npm:
mkdir graphql-tutorial
cd graphql-tutorial
npm init -y
Теперь установите необходимые пакеты. Основные зависимости: express (веб-фреймворк), @apollo/server (сервер GraphQL), graphql (ядро GraphQL) и cors (для кросс-доменных запросов).
npm install express @apollo/server graphql cors
Создайте файл index.js — в нем будет весь код сервера.
2. Создание схемы GraphQL и резолверов
Схема — это описание типов данных и операций (запросы, мутации). Резолверы — функции, которые возвращают данные для каждого поля.
Добавим простую модель данных — список книг. Каждая книга имеет id, title, author и year.
Откройте index.js и напишите следующий код:
const { ApolloServer } = require('@apollo/server');
const { startStandaloneServer } = require('@apollo/server/standalone');
const express = require('express');
const cors = require('cors');
// Имитация базы данных
let books = [
{ id: '1', title: 'Война и мир', author: 'Лев Толстой', year: 1869 },
{ id: '2', title: 'Преступление и наказание', author: 'Федор Достоевский', year: 1866 },
{ id: '3', title: 'Мастер и Маргарита', author: 'Михаил Булгаков', year: 1967 }
];
// Определение схемы (Type Definitions)
const typeDefs = `#graphql
type Book {
id: ID!
title: String!
author: String!
year: Int
}
type Query {
books: [Book]
book(id: ID!): Book
}
type Mutation {
addBook(title: String!, author: String!, year: Int): Book
deleteBook(id: ID!): Book
}
`;
// Резолверы
const resolvers = {
Query: {
books: () => books,
book: (parent, args) => books.find(book => book.id === args.id)
},
Mutation: {
addBook: (parent, args) => {
const newBook = {
id: String(books.length + 1),
title: args.title,
author: args.author,
year: args.year
};
books.push(newBook);
return newBook;
},
deleteBook: (parent, args) => {
const index = books.findIndex(book => book.id === args.id);
if (index === -1) return null;
const deletedBook = books.splice(index, 1)[0];
return deletedBook;
}
}
};
// Запуск сервера
async function startServer() {
const app = express();
app.use(cors());
const server = new ApolloServer({
typeDefs,
resolvers,
});
const { url } = await startStandaloneServer(server, {
listen: { port: 4000 },
});
console.log(`Сервер GraphQL запущен на ${url}`);
}
startServer();
Разберем код:
- typeDefs — определяет типы
Book,Query(запросы) иMutation(изменения). - resolvers — содержит функции для каждого поля. Для
booksвозвращается весь массив, дляbook— фильтрация по id. - startStandaloneServer — запускает сервер на порту 4000.
3. Тестирование API через GraphQL Playground
Запустите сервер командой node index.js. Откройте браузер и перейдите по адресу http://localhost:4000. Вы увидите интерфейс Apollo Sandbox (современная версия Playground).
Теперь можно выполнять запросы. Пример запроса на получение всех книг:
query {
books {
id
title
author
}
}
Ответ будет содержать только запрошенные поля:
{
"data": {
"books": [
{ "id": "1", "title": "Война и мир", "author": "Лев Толстой" },
{ "id": "2", "title": "Преступление и наказание", "author": "Федор Достоевский" },
{ "id": "3", "title": "Мастер и Маргарита", "author": "Михаил Булгаков" }
]
}
}
Попробуйте мутацию для добавления книги:
mutation {
addBook(title: "Анна Каренина", author: "Лев Толстой", year: 1877) {
id
title
}
}
Если мутация выполнена успешно, вы увидите новую книгу в ответе.
4. Расширение API: аргументы, вложенные запросы и обработка ошибок
GraphQL поддерживает вложенные запросы и сложные типы. Добавим тип Author и свяжем его с книгами.
Обновите typeDefs:
const typeDefs = `#graphql
type Author {
id: ID!
name: String!
books: [Book]
}
type Book {
id: ID!
title: String!
author: Author!
year: Int
}
type Query {
books: [Book]
book(id: ID!): Book
authors: [Author]
author(id: ID!): Author
}
type Mutation {
addBook(title: String!, authorId: ID!, year: Int): Book
deleteBook(id: ID!): Book
}
`;
Добавим массив авторов и изменим резолверы:
let authors = [
{ id: '1', name: 'Лев Толстой' },
{ id: '2', name: 'Федор Достоевский' },
{ id: '3', name