Введение в GraphQL и его преимущества
GraphQL — это язык запросов и среда выполнения для API, разработанный Facebook. В отличие от REST, где вы получаете фиксированные структуры данных, GraphQL позволяет клиенту запрашивать только то, что ему нужно. Это делает API более гибкими и эффективными.
В этом пошаговом туториале мы создадим полноценный GraphQL API на JavaScript с использованием Node.js. Вы научитесь настраивать сервер, определять схемы, обрабатывать запросы и мутации. К концу статьи у вас будет работающий API, готовый к интеграции в любой проект.
1. Настройка проекта и установка зависимостей
Для начала создадим новый проект Node.js и установим необходимые пакеты. Мы будем использовать apollo-server — популярную библиотеку для создания GraphQL серверов.
Шаг 1: Инициализация проекта
mkdir graphql-tutorial
cd graphql-tutorial
npm init -y
Шаг 2: Установка Apollo Server и GraphQL
npm install apollo-server graphql
После установки создадим основной файл index.js, где будет располагаться наш сервер.
2. Создание базового GraphQL сервера
Теперь напишем минимальный сервер, который будет принимать запросы и возвращать ответы. Для этого определим схему (schema) и резолверы (resolvers).
Шаг 1: Определение типа данных
Создадим простой тип Book с полями id, title и author.
const { ApolloServer, gql } = require('apollo-server');
// Схема GraphQL
const typeDefs = gql`
type Book {
id: ID!
title: String!
author: String!
}
type Query {
books: [Book]
book(id: ID!): Book
}
`;
// Пример данных (в реальном проекте — база данных)
const books = [
{ id: '1', title: 'Война и мир', author: 'Лев Толстой' },
{ id: '2', title: 'Преступление и наказание', author: 'Фёдор Достоевский' },
{ id: '3', title: 'Мастер и Маргарита', author: 'Михаил Булгаков' }
];
// Резолверы — функции, возвращающие данные
const resolvers = {
Query: {
books: () => books,
book: (parent, args) => books.find(book => book.id === args.id)
}
};
// Создание сервера
const server = new ApolloServer({ typeDefs, resolvers });
// Запуск сервера
server.listen().then(({ url }) => {
console.log(`🚀 Сервер готов по адресу: ${url}`);
});
Шаг 2: Запуск и тестирование
Выполните команду node index.js и откройте http://localhost:4000 в браузере. Вы увидите GraphQL Playground — инструмент для тестирования запросов.
Попробуйте выполнить простой запрос:
{
books {
title
author
}
}
Ответ должен содержать список книг с указанными полями.
3. Добавление мутаций и расширение схемы
Теперь добавим возможность изменять данные через мутации. Это позволит создавать, обновлять и удалять записи.
Шаг 1: Определение мутаций в схеме
const typeDefs = gql`
type Book {
id: ID!
title: String!
author: String!
}
input BookInput {
title: String!
author: String!
}
type Query {
books: [Book]
book(id: ID!): Book
}
type Mutation {
addBook(input: BookInput!): Book
deleteBook(id: ID!): Book
}
`;
Шаг 2: Реализация резолверов для мутаций
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.input.title,
author: args.input.author
};
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;
}
}
};
Шаг 3: Тестирование мутаций
Теперь в Playground можно выполнять мутации. Например, чтобы добавить новую книгу:
mutation {
addBook(input: { title: "Анна Каренина", author: "Лев Толстой" }) {
id
title
author
}
}
А чтобы удалить книгу по ID:
mutation {
deleteBook(id: "1") {
id
title
}
}
4. Оптимизация и обработка ошибок
В реальном проекте важно правильно обрабатывать ошибки и структурировать код. Рассмотрим несколько улучшений.
Шаг 1: Кастомные типы ошибок
Apollo Server поддерживает встроенные классы ошибок. Используйте ApolloError для возврата понятных сообщений.
const { ApolloError } = require('apollo-server');
const resolvers = {
Query: {
book: (parent, args) => {
const book = books.find(book => book.id === args.id);
if (!book) {
throw new ApolloError('Книга не найдена', 'BOOK_NOT_FOUND');
}
return book;
}
}
};
Шаг 2: Разделение на модули
Для больших проектов выносите схему и резолверы в отдельные файлы. Структура может выглядеть так:
schema.js— определение типов и схемыresolvers/— папка с резолверами для каждого типаmodels/— модели данных (например, для работы с БД)
Шаг 3: Работа с базой данных
Вместо статического массива подключите MongoDB или PostgreSQL. Пример с MongoDB и Mongoose:
const mongoose = require('mongoose');
const bookSchema = new mongoose.Schema({
title: String,
author: String
});
const Book = mongoose.model('Book', bookSchema);
const resolvers = {
Query: {
books: async () => await Book.find(),
book: async (parent, args) => await Book.findById(args.id)
},