GraphQL API с Go: пошаговый туториал для начинающих

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

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

Начать курс

Введение в GraphQL и Go

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

В этом туториале мы создадим простой GraphQL API на Go для управления списком книг. Вы узнаете, как настроить сервер, определить схему, написать резолверы и выполнять запросы и мутации.

Настройка проекта и установка библиотек

Инициализация модуля Go

Создайте новую директорию для проекта и инициализируйте модуль:

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

Установка библиотеки graphql-go

Мы будем использовать популярную библиотеку graphql-go/graphql. Установите её с помощью команды:

go get github.com/graphql-go/graphql
go get github.com/graphql-go/handler

Первая библиотека предоставляет ядро GraphQL, а вторая — HTTP-обработчик для интеграции с веб-сервером.

Создание структуры данных и схемы GraphQL

Определение типа Book

Создайте файл main.go и начните с определения структуры данных книги и соответствующего GraphQL-типа:

package main

import (
    "github.com/graphql-go/graphql"
    "github.com/graphql-go/handler"
    "net/http"
)

// Book — структура данных книги
type Book struct {
    ID     string `json:"id"`
    Title  string `json:"title"`
    Author string `json:"author"`
    Year   int    `json:"year"`
}

// Определяем GraphQL-тип для книги
var bookType = graphql.NewObject(
    graphql.ObjectConfig{
        Name: "Book",
        Fields: graphql.Fields{
            "id":     &graphql.Field{Type: graphql.String},
            "title":  &graphql.Field{Type: graphql.String},
            "author": &graphql.Field{Type: graphql.String},
            "year":   &graphql.Field{Type: graphql.Int},
        },
    },
)

Создание корневого запроса (Query)

Добавим in-memory хранилище книг и корневой запрос для получения списка и одной книги:

// Имитация базы данных
var books = []Book{
    {ID: "1", Title: "Война и мир", Author: "Лев Толстой", Year: 1869},
    {ID: "2", Title: "Преступление и наказание", Author: "Фёдор Достоевский", Year: 1866},
    {ID: "3", Title: "Мастер и Маргарита", Author: "Михаил Булгаков", Year: 1967},
}

// Корневой запрос
var queryType = graphql.NewObject(
    graphql.ObjectConfig{
        Name: "Query",
        Fields: graphql.Fields{
            "books": &graphql.Field{
                Type: graphql.NewList(bookType),
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    return books, nil
                },
            },
            "book": &graphql.Field{
                Type: bookType,
                Args: graphql.FieldConfigArgument{
                    "id": &graphql.ArgumentConfig{Type: graphql.String},
                },
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    id, _ := p.Args["id"].(string)
                    for _, book := range books {
                        if book.ID == id {
                            return book, nil
                        }
                    }
                    return nil, nil
                },
            },
        },
    },
)

Добавление мутаций (Mutation)

Мутации позволяют изменять данные. Добавим возможность добавлять новые книги:

// Мутация для добавления книги
var mutationType = graphql.NewObject(
    graphql.ObjectConfig{
        Name: "Mutation",
        Fields: graphql.Fields{
            "addBook": &graphql.Field{
                Type: bookType,
                Args: graphql.FieldConfigArgument{
                    "title":  &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.String)},
                    "author": &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.String)},
                    "year":   &graphql.ArgumentConfig{Type: graphql.NewNonNull(graphql.Int)},
                },
                Resolve: func(p graphql.ResolveParams) (interface{}, error) {
                    title, _ := p.Args["title"].(string)
                    author, _ := p.Args["author"].(string)
                    year, _ := p.Args["year"].(int)

                    newBook := Book{
                        ID:     fmt.Sprintf("%d", len(books)+1),
                        Title:  title,
                        Author: author,
                        Year:   year,
                    }
                    books = append(books, newBook)
                    return newBook, nil
                },
            },
        },
    },
)

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

Сборка схемы и запуск сервера

Теперь объединим всё в единую схему и запустим HTTP-сервер:

func main() {
    // Создаём схему GraphQL
    schema, err := graphql.NewSchema(
        graphql.SchemaConfig{
            Query:    queryType,
            Mutation: mutationType,
        },
    )
    if err != nil {
        panic(err)
    }

    // Настраиваем HTTP-обработчик
    h := handler.New(&handler.Config{
        Schema:   &schema,
        Pretty:   true,
        GraphiQL: true, // Включаем GraphiQL IDE для тестирования
    })

    // Запускаем сервер на порту 8080
    http.Handle("/graphql", h)
    fmt.Println("Сервер запущен на http://localhost:8080/graphql")
    http.ListenAndServe(":8080", nil)
}

Теперь вы можете запустить сервер командой go run main.go и открыть в браузере http://localhost:8080/graphql. Вы увидите интерфейс GraphiQL, где можно выполнять запросы.

Тестирование API

Запрос списка книг

Введите в левой панели GraphiQL:

{
  books {
    id
    title
    author
    year
  }
}

Вы получите JSON-ответ со всеми книгами из нашего хранилища.

Запрос одной книги по ID

{
  book(id: "2") {
    title
    author
  }
}

Мутация: добавление новой книги

mutation {
  addBook(title: "1984", author: "Джордж Оруэлл", year: 1949) {
    id
    title
    author
  }
}

После выполнения мутации новая книга появится в списке.

Блоги

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