GraphQL API с Go: пошаговый туториал на практике

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение

GraphQL — это современный язык запросов для API, разработанный Facebook. В отличие от REST, он позволяет клиенту запрашивать только необходимые данные, что снижает нагрузку на сеть и ускоряет разработку. В этом туториале мы создадим полноценный GraphQL API на языке Go (Golang) с использованием популярной библиотеки gqlgen. Вы узнаете, как настроить сервер, определить схему, написать резолверы и выполнить запросы. Статья ориентирована на разработчиков, знакомых с основами Go, но даже новички смогут повторить шаги благодаря подробным комментариям.

1. Настройка проекта и установка зависимостей

Первым делом создадим новый проект Go и установим необходимые пакеты.

Шаг 1: Инициализация модуля

mkdir graphql-go-tutorial
cd graphql-go-tutorial
go mod init graphql-go-tutorial

Шаг 2: Установка gqlgen

gqlgen — это генератор кода GraphQL для Go. Он автоматически создает резолверы на основе схемы.

go get github.com/99designs/gqlgen@latest

Шаг 3: Генерация скелета проекта

Запустите команду инициализации:

go run github.com/99designs/gqlgen init

После выполнения вы увидите структуру:

  • graph/ — папка со схемой и резолверами
  • server.go — точка входа
  • gqlgen.yml — конфигурация генератора

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

Схема (schema) описывает типы данных и операции API. Откройте файл graph/schema.graphqls и замените его содержимое на следующее:

type User {
  id: ID!
  name: String!
  email: String!
  age: Int!
}

type Query {
  users: [User!]!
  user(id: ID!): User
}

type Mutation {
  createUser(name: String!, email: String!, age: Int!): User!
  updateUser(id: ID!, name: String, email: String, age: Int): User
  deleteUser(id: ID!): Boolean
}

Здесь мы определили:

  • User — объект с полями id, name, email, age.
  • Query — операции чтения (получить всех пользователей или одного по ID).
  • Mutation — операции записи (создать, обновить, удалить).

После сохранения запустите генерацию кода:

go run github.com/99designs/gqlgen generate

Это создаст файлы models_gen.go (типы Go) и generated.go (интерфейсы резолверов).

3. Реализация резолверов и хранилища данных

Теперь нужно написать логику для обработки запросов. Для простоты будем хранить данные в памяти (срез структур).

Шаг 1: Создаем хранилище

Создайте файл graph/store.go:

package graph

import "sync"

type Store struct {
  mu    sync.RWMutex
  users []User
  nextID int
}

func NewStore() *Store {
  return &Store{
    users: []User{
      {ID: "1", Name: "Alice", Email: "alice@example.com", Age: 30},
      {ID: "2", Name: "Bob", Email: "bob@example.com", Age: 25},
    },
    nextID: 3,
  }
}

func (s *Store) GetUsers() []User {
  s.mu.RLock()
  defer s.mu.RUnlock()
  return s.users
}

func (s *Store) GetUser(id string) *User {
  s.mu.RLock()
  defer s.mu.RUnlock()
  for _, u := range s.users {
    if u.ID == id {
      return &u
    }
  }
  return nil
}

func (s *Store) CreateUser(name, email string, age int) User {
  s.mu.Lock()
  defer s.mu.Unlock()
  user := User{
    ID:    fmt.Sprintf("%d", s.nextID),
    Name:  name,
    Email: email,
    Age:   age,
  }
  s.nextID++
  s.users = append(s.users, user)
  return user
}

func (s *Store) UpdateUser(id string, name, email *string, age *int) *User {
  s.mu.Lock()
  defer s.mu.Unlock()
  for i, u := range s.users {
    if u.ID == id {
      if name != nil {
        s.users[i].Name = *name
      }
      if email != nil {
        s.users[i].Email = *email
      }
      if age != nil {
        s.users[i].Age = *age
      }
      return &s.users[i]
    }
  }
  return nil
}

func (s *Store) DeleteUser(id string) bool {
  s.mu.Lock()
  defer s.mu.Unlock()
  for i, u := range s.users {
    if u.ID == id {
      s.users = append(s.users[:i], s.users[i+1:]...)
      return true
    }
  }
  return false
}

Не забудьте добавить импорт "fmt".

Шаг 2: Реализуем резолверы

Откройте graph/schema.resolvers.go. Там уже есть заготовки. Замените их на:

package graph

import (
  "context"
  "graphql-go-tutorial/graph/model"
)

func (r *mutationResolver) CreateUser(ctx context.Context, name string, email string, age int) (*model.User, error) {
  user := r.Store.CreateUser(name, email, age)
  return &user, nil
}

func (r *mutationResolver) UpdateUser(ctx context.Context, id string, name *string, email *string, age *int) (*model.User, error) {
  user := r.Store.UpdateUser(id, name, email, age)
  if user == nil {
    return nil, fmt.Errorf("user not found")
  }
  return user, nil
}

func (r *mutationResolver) DeleteUser(ctx context.Context, id string) (bool, error) {
  return r.Store.DeleteUser(id), nil
}

func (r *queryResolver) Users(ctx context.Context) ([]*model.User, error) {
  users := r.Store.GetUsers()
  result := make([]*model.User, len(users))
  for i, u := range users {
    result[i] = &u
  }
  return result, nil
}

func (r *queryResolver) User(ctx context.Context, id string) (*model.User, error) {
  user := r.Store.GetUser(id)
  if user == nil {
    return nil, fmt.Errorf("user not found")
  }
  return user, nil
}

Шаг 3: Подключаем хранилище в резолвер

Блоги

Рекомендаций по книгам