Гид по технологиям

Как создать GraphQL API с MongoDB и Apollo Server

6 min read API Обновлено 10 Apr 2026
GraphQL с MongoDB и Apollo Server
GraphQL с MongoDB и Apollo Server

Открытый ноутбук с набором кода в текстовом редакторе

Зачем выбирать GraphQL вместо REST

REST остаётся популярным, но его фиксированная структура эндпоинтов часто приводит к избыточной передаче данных и множественным обращениями за связанными сущностями. GraphQL даёт один входной эндпоинт и позволяет клиенту описать, какие поля ему нужны. Это сокращает объём передаваемых данных и упрощает эволюцию API.

Короткое определение: GraphQL — язык запросов и спецификация для реализации API, где клиент запрашивает конкретную форму данных, а сервер отвечает строго в этом же формате.

Компоненты архитектуры GraphQL (в двух строках)

  • Схема (Schema): описывает типы данных и операции.
  • Запросы (Query): чтение данных.
  • Мутации (Mutation): изменение данных.

Иллюстрация с двумя веб-серверами, развернутыми в сети

Подготовка MongoDB

  1. Установите MongoDB локально или создайте бесплатный кластер в облаке (например, MongoDB Atlas).
  2. Скопируйте строку подключения (connection URI). Пример локально: mongodb://localhost:27017

Важно: не храните секреты URI в исходниках — используйте переменные окружения.

Источник кода этого проекта доступен в репозитории GitHub (указан в исходном материале).

Создание проекта и установка зависимостей

Откройте терминал и выполните:

mkdir graphql-API-mongoDB  
cd graphql-API-mongoDB

Инициализируйте npm-проект:

npm init --yes

Установите пакеты:

npm install apollo-server graphql mongoose

Создайте файл index.js в корне проекта.

Настройка Apollo Server

Вставьте в index.js следующий код (файл из исходного примера):

const { ApolloServer } = require('apollo-server');  
const mongoose = require('mongoose');  
const typeDefs = require("./graphql/typeDefs");  
const resolvers = require("./graphql/resolvers");  
  
const server = new ApolloServer({  
    typeDefs,   
    resolvers   
});  
  
const MONGO_URI = 'mongodb://localhost:27017';  
  
mongoose  
  .connect(MONGO_URI, {  
    useNewUrlParser: true,  
    useUnifiedTopology: true,  
  })  
  .then(() => {  
    console.log(`Db Connected`);  
    return server.listen({ port: 5000 });  
  })  
  .then((res) => {  
    console.log(`Server running at ${res.url}`);  
  })  
  .catch(err => {  
    console.log(err.message);  
  });  

Этот код поднимает локальный GraphQL-сервер и подключается к MongoDB. Сервер будет слушать порт 5000 после успешного подключения.

Важно: для боевого окружения настраивайте переменные окружения и систему логирования.

Определение модели данных в Mongoose

Создайте папку models и файл models/dataModel.js с содержимым:

const {model, Schema} = require('mongoose');  
  
const employeeSchema = new Schema({  
    name: String,  
    department: String,  
    salary: String,  
});  
  
module.exports = model('Employee', employeeSchema);

Замечание: типы полей можно расширить (Number, Date, вложенные схемы). Для зарплаты удобно использовать Number и хранить валюту отдельно.

Определение схемы GraphQL

Создайте папку graphql и два файла: graphql/typeDefs.js и graphql/resolvers.js

Содержимое typeDefs.js из примера:

const {gql} = require("apollo-server");  
  
const typeDefs = gql`  
  type Employee {  
    id: ID!  
    name: String  
    department: String  
    salary: String  
  }  
  input EmployeeInput {  
    name: String  
    department: String  
    salary: String  
  }  
  type Query {   
    getEmployee(id: ID): Employee #return Employee by id  
    employees: [Employee] #return array of Employees  
  }  
  type Mutation {  
    createEmployee(employeeInput: EmployeeInput): Employee  
    updateEmployee(id: ID, employeeInput: EmployeeInput): Boolean  
    deleteEmployee(id: ID): Boolean  
  }  
`;  
  
module.exports = typeDefs;

Пояснение: типы и input-типы отделяют схему чтения от схемы записи; для сложных операций добавляйте валидацию и отдельные payload-типы.

Реализация резолверов

Добавьте код из примера в graphql/resolvers.js. Резолверы читают и изменяют данные через модель Mongoose.

Часть файла (Query):

const Employee= require("../models/employeesModel");  
  
// GraphQL Resolvers  
const resolvers = {  
  Query: {  
    employees: async () => {  
      try {  
        const employees = await Employee.find({});  
        return employees;  
      } catch (error) {  
        console.error(error);  
        throw new Error('Failed to fetch employees');  
      }  
    },  
    getEmployee: async (parent, args) => {  
      try {  
        const employee = await Employee.findById(args.id);  
        return employee;  
      } catch (error) {  
        console.error(error);  
        throw new Error('Failed to fetch employee by ID');  
      }  
    },  
  },  
  

Часть файла (Mutation):

  Mutation: {  
    async createEmployee (_, { employeeInput: { name, department, salary } }) {  
      const newEmployee = new Employee({  
        name: name,  
        department: department,  
        salary: salary  
      });  
  
      const response =  await newEmployee.save();  
      console.log(newEmployee);  
  
      return {  
        id: response._id,  
        ...response._doc  
      }  
    },  
  
    async updateEmployee (_, {id, employeeInput: {name, department, salary}}) {  
      const updatedEmployee = await Employee.updateOne(  
        { _id: id },  
        { name, department, salary }  
      );  
  
      if (!updatedEmployee) {  
        throw new Error(`Employee with ID: ${id} not found`);  
      }  
  
      return true;  // Return a boolean value indicating update success  
    },  
  
    async deleteEmployee (_, {id}) {  
      const deletedEmployee = await Employee.deleteOne({ _id: id });  
         
      if (!deletedEmployee || deletedEmployee.deletedCount === 0) {  
          throw new Error(`Employee with ID ${id} not found`);  
      }  
  
      return true;  // Return a boolean value indicating deletion success  
    },  
},  
};  
  
module.exports =  resolvers;

После этого можно запустить сервер командой:

node index.js

Когда соединение с БД установлено, сервер будет доступен на http://localhost:5000 и вы сможете открыть GraphQL Playground для тестирования.

Реализация GraphQL API в браузере через GraphQL Playground

Типовые примеры запросов и мутаций

Пример запроса всех сотрудников:

query {
  employees {
    id
    name
    department
    salary
  }
}

Создание сотрудника:

mutation {
  createEmployee(employeeInput: {name: "Ivan", department: "Dev", salary: "5000"}) {
    id
    name
  }
}

Эти примеры можно выполнять в встроенном GraphQL Playground.

Когда GraphQL не подходит (контрпример)

  • Простые CRUD-сервисы с предсказуемыми моделями и малой нагрузкой — REST может быть проще.
  • API с жёсткими требованиями к кешированию HTTP-уровня (CDN, прокси) иногда проще реализовать через REST/HTTP-кэш.
  • Сервисы с бинарными потоками (высокопроизводительная потоковая передача) — лучше gRPC или специализированные решения.

Альтернативные подходы и когда их выбрать

  • REST: простота, широкая совместимость, хорош для публичных API с чёткими ресурсами.
  • gRPC: низкая задержка, строгая контрактность, идеально для микросервисов внутри дата-центра.
  • Falcor/JSON: редкие альтернативы для хранения данных на клиенте.

Модель принятия решения (Flowchart)

flowchart TD
  A[Нужно ли клиентам выбирать поля?] -->|Да| B[GraphQL]
  A -->|Нет| C[REST]
  B --> D{Есть ли heavy-streaming?}
  D -->|Да| E[gRPC/Streaming]
  D -->|Нет| F[GraphQL подходит]

Безопасность и харднинг

  • Валидация входных данных на уровне резолверов и модели.
  • Ограничение глубины запросов и комплексности (query depth, query complexity) чтобы защититься от DoS.
  • Авторизация на уровне полей и резолверов (field-level authorization).
  • Лимитирование частоты запросов (rate limiting) и защита по IP/токенам.
  • Храните секреты (URI, ключи) в менеджере секретов или переменных окружения.

Конфиденциальность и GDPR

  • Если вы храните персональные данные сотрудников, определите правовую основу обработки.
  • Обеспечьте удаление и экспорт данных по запросам пользователя (право на удаление/экспорт).
  • Логирование: избегайте дампов персональных данных в логах.

Чек-листы по ролям

Разработчик:

  • Покрыть резолверы юнит-тестами.
  • Добавить валидацию входных данных.
  • Обработать ошибки и вернуть человекочитаемые сообщения.

DevOps/Инженер инфраструктуры:

  • Настроить резервное копирование MongoDB.
  • Обеспечить мониторинг и alerting (CPU, память, количество подключений).
  • Конфигурировать секреты и доступы.

Продукт/PO:

  • Согласовать схему данных и обязательные поля.
  • Определить SLA для API.

Playbook: развертывание и выпуск

  1. Подготовка: собрать Docker-образ с node и зависимостями.
  2. Тестирование: прогнать unit и интеграционные тесты, покрыть сценарии создания/обновления/удаления.
  3. Предрелиз: выкатить на staging, прогнать нагрузочные тесты, проверить политики rate limiting.
  4. Релиз: перевести трафик на новую версию, наблюдать метрики, откат при критических ошибках.

Критерии приёмки

  • Все запросы из спецификации отдают ожидаемые поля.
  • Мутации корректно меняют состояние БД и возвращают валидный payload.
  • Для несуществующего ID возвращается корректная ошибка.
  • Тесты проходят в CI без предупреждений безопасности.

Тестовые сценарии

  • Позитив: создать сотрудника, получить его по id, обновить данные, удалить и убедиться, что запрос по id возвращает null/ошибку.
  • Негатив: попытаться удалить несуществующий id и получить понятную ошибку.
  • Нагрузочный: 1000 параллельных запросов на чтение — следить за временем ответа и ошибками.

Отладка и откат (runbook)

Проблемы при старте сервера:

  • Проверьте доступность MongoDB и корректность MONGO_URI.
  • Проверяйте логи на ошибки Mongoose.

Если в проде появляются ошибки чтения/записи:

  • Переключите трафик на предыдущую стабильную версию.
  • Проверьте схемы и миграции модели.

Миграция с REST на GraphQL — советы

  • Начните с малого: вынесите наиболее «болезненный» REST-эндпоинт в GraphQL.
  • Используйте слой BFF (Backend for Frontend), чтобы постепенно переводить клиентов.
  • Поддерживайте версионирование на уровне полей и deprecated-полей в схеме.

Локальные альтернативы и подводные камни

  • Если в вашей локальной среде Windows, убедитесь, что MongoDB корректно настроен как служба.
  • В облачных окружениях провайдеров IPv6 может поменять поведение сетевых правил.

Социальный превью

OG title: GraphQL с MongoDB и Apollo Server OG description: Быстрый старт: GraphQL API на Node.js, Apollo Server и MongoDB — настройка, резолверы, безопасность и тесты.

Короткое объявление (100–200 слов):

В этом руководстве показано, как за несколько шагов развернуть GraphQL API на Node.js с использованием Apollo Server и MongoDB. Вы создадите модель сотрудника в Mongoose, определите схему GraphQL, реализуете резолверы для запросов и мутаций, а также получите чек-листы по безопасности, тестированию и CI/CD. Подойдёт как для прототипа, так и для постепенного внедрения GraphQL в существующие проекты.

Краткое резюме

  • GraphQL даёт гибкость и экономию трафика благодаря выбору полей клиентом.
  • Apollo Server упрощает запуск на Node.js, а Mongoose — работу с MongoDB.
  • Не забывайте об ограничениях: глубина запросов, авторизация, логирование и GDPR.

Ключевые действия, чтобы начать: 1) поднять MongoDB, 2) установить зависимости, 3) создать схему и резолверы, 4) написать тесты и настроить мониторинг.

Ключевые выводы

  1. GraphQL подходит, если клиентам нужно выбирать поля и агрегировать данные из разных источников.
  2. Для простых CRUD-сервисов иногда REST остаётся более явным и простым вариантом.
  3. Безопасность и контроль сложности запросов — обязательны при публичном доступе.
Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Папка автозагрузки Windows — найти и управлять
Windows

Папка автозагрузки Windows — найти и управлять

Отключить исчезновение окон в macOS Sonoma
macOS

Отключить исчезновение окон в macOS Sonoma

Отключить клавишу Globe на Mac
macOS

Отключить клавишу Globe на Mac

Отключить миниатюры скриншотов на Mac
macOS

Отключить миниатюры скриншотов на Mac

RAW в JPEG на Mac — через Preview
Фото

RAW в JPEG на Mac — через Preview

Удалить фон с фото на Mac — быстро и без программ
macOS

Удалить фон с фото на Mac — быстро и без программ