Как написать REST API на Go за 30 минут: практическое руководство

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

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

Начать курс

Введение: зачем Go для REST API?

Go (Golang) — современный компилируемый язык с встроенной поддержкой конкурентности и высокой производительностью. REST API на Go работает быстро, потребляет мало памяти и легко масштабируется. За 30 минут вы создадите полноценный REST сервис с GET, POST, PUT и DELETE запросами, используя только стандартную библиотеку.

В этом руководстве мы напишем API для управления списком задач (Todo app). Вы узнаете, как настроить маршрутизацию, обрабатывать JSON, добавлять middleware для логирования и правильно возвращать ошибки. Без сторонних фреймворков — только чистый Go!

1. Настройка проекта и структура

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

mkdir go-todo-api
cd go-todo-api
go mod init todo-api

Структура проекта будет простой:

  • main.go — точка входа и запуск сервера
  • handlers.go — обработчики запросов
  • models.go — структуры данных
  • middleware.go — промежуточное ПО

Создайте файл models.go с структурой задачи:

package main

type Task struct {
    ID        int    `json:"id"`
    Title     string `json:"title"`
    Completed bool   `json:"completed"`
}

Мы будем хранить задачи в памяти (срез tasks) для простоты. В реальном проекте используйте базу данных.

2. Маршрутизация и обработчики

Go использует встроенный пакет net/http. Создадим файл handlers.go с основными обработчиками.

2.1. GET /tasks — получение всех задач

package main

import (
    "encoding/json"
    "net/http"
    "strconv"
)

var tasks = []Task{}
var nextID = 1

func getTasks(w http.ResponseWriter, r *http.Request) {
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(tasks)
}

2.2. POST /tasks — создание новой задачи

func createTask(w http.ResponseWriter, r *http.Request) {
    var newTask Task
    if err := json.NewDecoder(r.Body).Decode(&newTask); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }
    newTask.ID = nextID
    nextID++
    tasks = append(tasks, newTask)
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(newTask)
}

2.3. PUT /tasks/{id} — обновление задачи

func updateTask(w http.ResponseWriter, r *http.Request) {
    idStr := r.PathValue("id")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, "Invalid task ID", http.StatusBadRequest)
        return
    }
    
    var updatedTask Task
    if err := json.NewDecoder(r.Body).Decode(&updatedTask); err != nil {
        http.Error(w, "Invalid JSON", http.StatusBadRequest)
        return
    }
    
    for i, t := range tasks {
        if t.ID == id {
            tasks[i].Title = updatedTask.Title
            tasks[i].Completed = updatedTask.Completed
            w.Header().Set("Content-Type", "application/json")
            json.NewEncoder(w).Encode(tasks[i])
            return
        }
    }
    http.Error(w, "Task not found", http.StatusNotFound)
}

2.4. DELETE /tasks/{id} — удаление задачи

func deleteTask(w http.ResponseWriter, r *http.Request) {
    idStr := r.PathValue("id")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, "Invalid task ID", http.StatusBadRequest)
        return
    }
    
    for i, t := range tasks {
        if t.ID == id {
            tasks = append(tasks[:i], tasks[i+1:]...)
            w.WriteHeader(http.StatusNoContent)
            return
        }
    }
    http.Error(w, "Task not found", http.StatusNotFound)
}

Обратите внимание: r.PathValue("id") доступно с Go 1.22. Если у вас более старая версия, используйте r.URL.Path или gorilla/mux.

3. Middleware для логирования

Middleware позволяют выполнять код до и после обработчика. Создадим простой логгер в middleware.go:

package main

import (
    "log"
    "net/http"
    "time"
)

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        log.Printf("Started %s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
        log.Printf("Completed %s in %v", r.URL.Path, time.Since(start))
    })
}

4. Главный файл main.go

Теперь объединим всё в main.go:

package main

import (
    "log"
    "net/http"
)

func main() {
    mux := http.NewServeMux()
    
    // Роутинг
    mux.HandleFunc("GET /tasks", getTasks)
    mux.HandleFunc("POST /tasks", createTask)
    mux.HandleFunc("PUT /tasks/{id}", updateTask)
    mux.HandleFunc("DELETE /tasks/{id}", deleteTask)
    
    // Применяем middleware
    handler := loggingMiddleware(mux)
    
    log.Println("Server started on :8080")
    log.Fatal(http.ListenAndServe(":8080", handler))
}

Запустите сервер:

go run .

Тестируйте с помощью curl или Postman:

# Создать задачу
curl -X POST http://localhost:8080/tasks \
  -H "Content-Type: application/json" \
  -d '{"title":"Изучить Go","completed":false}'

# Получить все задачи
curl http://localhost:8080/tasks

5. Обработка ошибок и CORS

Добавим поддержку CORS для работы с фронтендом. Создайте новый middleware:

func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type")
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }
        next.ServeHTTP(w, r)
    })
}

Об

Блоги

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