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

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

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

Начать курс

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

Go (или Golang) — это современный компилируемый язык программирования, созданный в Google. Он сочетает в себе производительность C++ и простоту Python, что делает его идеальным выбором для создания высоконагруженных веб-сервисов и REST API. В отличие от многих других языков, Go имеет встроенную поддержку HTTP-сервера, многопоточности и быструю компиляцию.

В этом руководстве мы шаг за шагом создадим полноценный REST API для управления списком задач (Todo List). Вы научитесь:

  • Настраивать HTTP-сервер на Go
  • Работать с JSON-запросами и ответами
  • Реализовывать CRUD-операции (Create, Read, Update, Delete)
  • Тестировать API с помощью curl или Postman

Весь процесс займет у вас не более 30 минут. Поехали!

1. Установка и настройка проекта

Прежде чем писать код, убедитесь, что у вас установлен Go. Проверить это можно командой:

go version

Если Go не установлен, скачайте последнюю версию с официального сайта.

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

mkdir go-rest-api
cd go-rest-api
go mod init go-rest-api

Теперь создайте файл main.go — в нем будет весь наш код. Для удобства работы с маршрутами мы будем использовать стандартную библиотеку net/http, которая уже встроена в Go. Никаких сторонних зависимостей не требуется — это одно из главных преимуществ языка.

2. Определяем структуру данных и хранилище

Начнем с описания модели данных. Для простоты будем хранить задачи в памяти (в слайсе), а не в базе данных. Создадим структуру Task и глобальный счетчик ID:

package main

import (
    "encoding/json"
    "log"
    "net/http"
    "strconv"
    "strings"
    "sync"
)

// Task представляет задачу
type Task struct {
    ID    int    `json:"id"`
    Title string `json:"title"`
    Done  bool   `json:"done"`
}

// Хранилище задач (в памяти)
var (
    tasks   []Task
    nextID  int = 1
    mu      sync.Mutex
)

Обратите внимание на sync.Mutex — он нужен для потокобезопасного доступа к данным, так как Go-сервер может обрабатывать запросы параллельно.

3. Реализация CRUD-операций

Теперь напишем основные обработчики для каждой HTTP-методы. Мы будем использовать паттерн маршрутизации по URL:

  • GET /tasks — получить все задачи
  • POST /tasks — создать новую задачу
  • GET /tasks/{id} — получить задачу по ID
  • PUT /tasks/{id} — обновить задачу
  • DELETE /tasks/{id} — удалить задачу

3.1. Получение всех задач (GET /tasks)

func getTasks(w http.ResponseWriter, r *http.Request) {
    mu.Lock()
    defer mu.Unlock()

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(tasks)
}

3.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
    }

    mu.Lock()
    newTask.ID = nextID
    nextID++
    tasks = append(tasks, newTask)
    mu.Unlock()

    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(newTask)
}

3.3. Получение, обновление и удаление по ID

Для этих операций нам нужно извлечь ID из URL. В стандартной библиотеке Go нет встроенного парсера путей, поэтому мы напишем простую утилиту:

// extractID извлекает ID из URL вида /tasks/123
func extractID(path string) (int, bool) {
    parts := strings.Split(strings.Trim(path, "/"), "/")
    if len(parts) != 2 || parts[0] != "tasks" {
        return 0, false
    }
    id, err := strconv.Atoi(parts[1])
    if err != nil {
        return 0, false
    }
    return id, true
}

func handleTaskByID(w http.ResponseWriter, r *http.Request) {
    id, ok := extractID(r.URL.Path)
    if !ok {
        http.Error(w, "Invalid ID", http.StatusBadRequest)
        return
    }

    mu.Lock()
    defer mu.Unlock()

    for i, t := range tasks {
        if t.ID == id {
            switch r.Method {
            case http.MethodGet:
                w.Header().Set("Content-Type", "application/json")
                json.NewEncoder(w).Encode(t)
                return
            case http.MethodPut:
                var updated Task
                if err := json.NewDecoder(r.Body).Decode(&updated); err != nil {
                    http.Error(w, "Invalid JSON", http.StatusBadRequest)
                    return
                }
                updated.ID = id
                tasks[i] = updated
                w.Header().Set("Content-Type", "application/json")
                json.NewEncoder(w).Encode(updated)
                return
            case http.MethodDelete:
                tasks = append(tasks[:i], tasks[i+1:]...)
                w.WriteHeader(http.StatusNoContent)
                return
            }
        }
    }
    http.Error(w, "Task not found", http.StatusNotFound)
}

4. Настройка маршрутизации и запуск сервера

Теперь объединим все обработчики в единую маршрутизацию. В Go для этого используется http.HandleFunc. Мы будем различать методы внутри обработчика:

func main() {
    // Маршрут для /tasks (без ID)
    http.HandleFunc("/tasks", func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case http.MethodGet:
            getTasks(w, r)
        case http.MethodPost:
            createTask(w, r)
        default:
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        }
    })

    // Маршрут для /tasks/{id}
    http.HandleFunc("/tasks/", handleTaskByID)

    // Запуск сервера на порту 8080
    log.Println("Server started on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

                
                

Блоги

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