Введение в 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
}
}
После выполнения мутации новая книга появится в списке.