Гид по технологиям

CRUD API на Go с Gin и MongoDB

6 min read Бэкенд Обновлено 04 Dec 2025
CRUD API на Go с Gin и MongoDB
CRUD API на Go с Gin и MongoDB

Редактор кода с фрагментами кода

Golang — один из высокооплачиваемых и востребованных языков программирования. В сочетании с фреймворками вроде Gin, Revel или gorilla/mux Go отлично подходит для создания API. В этом руководстве вы шаг за шагом создадите CRUD API на Go с использованием Gin и MongoDB.

Что вы получите из этого руководства

  • Рабочий CRUD API на Go (создать, прочитать, обновить, удалить).
  • Пример структуры проекта и кода для реального приложения.
  • Советы по безопасности, тестированию и деплою.

Начальная настройка и установка

Установите Golang на вашу машину, если он ещё не установлен. Затем создайте корневую папку проекта и инициализируйте модуль Go в этой папке.

Откройте терминал, перейдите в корень проекта и выполните:

go mod init module_name

Откройте файл go.mod — вы увидите имя вашего модуля (например, CRUD_API). Все собственные пакеты будут импортироваться от этого модуля, например:

import "CRUD_API/package-directory-name"

Установите зависимости. Для роутинга используем Gin:

go get github.com/gin-gonic/gin

Для работы с MongoDB установите официальный драйвер:

go get go.mongodb.org/mongo-driver/mongo

Важно: храните конфиденциальные строки подключения (URI) в .env или в системе управления секретами — не в коде.

Как подключить Go к MongoDB

Для подключения требуется MongoDB URI. Пример локального подключения к MongoDB:

Mongo_URL = "mongodb://127.0.0.1:27017"

Создайте папку databases и файл database.go — пакет для подключения к базе. Ниже пример функции подключения:

package database

import (
    "context"
    "fmt"
    "log"
    "time"
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

func ConnectDB() *mongo.Client {
    Mongo_URL := "mongodb://127.0.0.1:27017"
    client, err := mongo.NewClient(options.Client().ApplyURI(Mongo_URL))

    if err != nil {
        log.Fatal(err)
    }

    ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
    err = client.Connect(ctx)
    defer cancel()

    if err != nil {
        log.Fatal(err)
    }

    fmt.Println("Connected to mongoDB")
    return client
}

Определение: Context — структурированный способ задавать таймауты и отмены операций; primitive.ObjectID — тип идентификатора документа в MongoDB.

Создание коллекции

MongoDB хранит данные в коллекциях. Создайте папку Collection и файл getCollection.go, который вернёт ссылку на коллекцию в базе:

package getcollection

import (
    "go.mongodb.org/mongo-driver/mongo"
)

func GetCollection(client *mongo.Client, collectionName string) *mongo.Collection {
    collection := client.Database("myGoappDB").Collection("Posts")
    return collection
}

Здесь база называется myGoappDB, коллекция — Posts. При желании вынесите имя БД и коллекции в константы или конфиг.

Создание модели данных

Создайте папку model и файл model.go с определением структуры поста:

package model

import (
    "go.mongodb.org/mongo-driver/bson/primitive"
)

type Post struct {
    ID      primitive.ObjectID
    Title   string
    Article string
}

Совет: добавляйте теги BSON/JSON к полям модели для надёжного маршалинога/анмаршалинога (например, bson:"_id,omitempty" json:"id"). Это облегчает сериализацию и работу с клиентами.

Создание CRUD API на Go

Создайте папку routes; в ней — файлы create.go, read.go, update.go, delete.go. Каждый файл экспортирует обработчик в пакете routes.

POST: создание записи

Файл routes/create.go — обработчик создания нового поста:

package routes

import (
    getcollection "CRUD_API/Collection"
    database "CRUD_API/databases"
    model "CRUD_API/model"
    "context"
    "log"
    "net/http"
    "time"
    "github.com/gin-gonic/gin"
    "go.mongodb.org/mongo-driver/bson/primitive"
)

func CreatePost(c *gin.Context) {
    var DB = database.ConnectDB()
    var postCollection = getcollection.GetCollection(DB, "Posts")
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    post := new(model.Posts)
    defer cancel()

    if err := c.BindJSON(&post); err != nil {
        c.JSON(http.StatusBadRequest, gin.H{"message": err})
        log.Fatal(err)
        return
    }

    postPayload := model.Posts{
        Id:      primitive.NewObjectID(),
        Title:   post.Title,
        Article: post.Article,
    }

    result, err := postCollection.InsertOne(ctx, postPayload)

    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"message": err})
        return
    }

    c.JSON(http.StatusCreated, gin.H{"message": "Posted successfully", "Data": map[string]interface{}{"data": result}})
}

Примечание: проверяйте ошибки BindJSON и не используйте log.Fatal в обработчиках — лучше возвращать корректный HTTP-ответ и логировать отдельно.

GET: чтение одной записи

Файл routes/read.go — чтение документа по id:

package routes

import (
    getcollection "CRUD_API/Collection"
    database "CRUD_API/databases"
    model "CRUD_API/model"
    "context"
    "net/http"
    "time"
    "github.com/gin-gonic/gin"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/primitive"
)

func ReadOnePost(c *gin.Context) {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    var DB = database.ConnectDB()
    var postCollection = getcollection.GetCollection(DB, "Posts")

    postId := c.Param("postId")
    var result model.Posts

    defer cancel()

    objId, _ := primitive.ObjectIDFromHex(postId)

    err := postCollection.FindOne(ctx, bson.M{"id": objId}).Decode(&result)

    res := map[string]interface{}{"data": result}

    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"message": err})
        return
    }

    c.JSON(http.StatusCreated, gin.H{"message": "success!", "Data": res})
}

Совет: используйте корректные поля фильтрации — обычно идентификатор в MongoDB хранится в поле _id, а не id.

PUT: обновление записи

Файл routes/update.go — обновление документа по id:

package routes

import (
    getcollection "CRUD_API/Collection"
    database "CRUD_API/databases"
    model "CRUD_API/model"
    "context"
    "net/http"
    "time"
    "github.com/gin-gonic/gin"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/primitive"
)

func UpdatePost(c *gin.Context) {
    ctx, cancel := context.WithTimeout(context.Background(), 10 * time.Second)
    var DB = database.ConnectDB()
    var postCollection = getcollection.GetCollection(DB, "Posts")

    postId := c.Param("postId")
    var post model.Posts

    defer cancel()

    objId, _ := primitive.ObjectIDFromHex(postId)

    if err := c.BindJSON(&post); err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"message": err})
        return
    }

    edited := bson.M{"title": post.Title, "article": post.Article}

    result, err := postCollection.UpdateOne(ctx, bson.M{"id": objId}, bson.M{"$set": edited})

    res := map[string]interface{}{"data": result}

    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"message": err})
        return
    }

    if result.MatchedCount < 1 {
        c.JSON(http.StatusInternalServerError, gin.H{"message": "Data doesn't exist"})
        return
    }

    c.JSON(http.StatusCreated, gin.H{"message": "data updated successfully!", "Data": res})
}

Обратите внимание: проверяйте matched/modified count, чтобы убедиться, что обновление действительно произошло.

DELETE: удаление записи

Файл routes/delete.go — удаление документа по id:

package routes

import (
    getcollection "CRUD_API/Collection"
    database "CRUD_API/databases"
    "context"
    "net/http"
    "time"
    "github.com/gin-gonic/gin"
    "go.mongodb.org/mongo-driver/bson"
    "go.mongodb.org/mongo-driver/bson/primitive"
)

func DeletePost(c *gin.Context) {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    var DB = database.ConnectDB()
    postId := c.Param("postId")

    var postCollection = getcollection.GetCollection(DB, "Posts")
    defer cancel()
    objId, _ := primitive.ObjectIDFromHex(postId)
    result, err := postCollection.DeleteOne(ctx, bson.M{"id": objId})
    res := map[string]interface{}{"data": result}

    if err != nil {
        c.JSON(http.StatusInternalServerError, gin.H{"message": err})
        return
    }

    if result.DeletedCount < 1 {
        c.JSON(http.StatusInternalServerError, gin.H{"message": "No data to delete"})
        return
    }

    c.JSON(http.StatusCreated, gin.H{"message": "Article deleted successfully", "Data": res})
}

Проверьте, что используете корректное поле _id и корректно обрабатываете ошибки преобразования hex->ObjectID.

Файл запускa main.go

Проект должен иметь примерно такую структуру (файловая схема показана ниже):

Структура проекта Golang CRUD

main.go отвечает за запуск HTTP-сервера и привязку маршрутов:

package main

import (
    routes "CRUD_API/routes"
    "github.com/gin-gonic/gin"
)

func main() {
    router := gin.Default()

    router.POST("/", routes.CreatePost)

    // called as localhost:3000/getOne/{id}
    router.GET("getOne/:postId", routes.ReadOnePost)

    // called as localhost:3000/update/{id}
    router.PUT("/update/:postId", routes.UpdatePost)

    // called as localhost:3000/delete/{id}
    router.DELETE("/delete/:postId", routes.DeletePost)

    router.Run("localhost: 3000")
}

Запуск сервера из корня проекта:

go run main.go

После запуска сервер будет доступен на localhost:3000.

Важно: для продакшена используйте переменные окружения и конфигурационные файлы, настройте TLS и корректный адрес прослушивания (обычно :80 или :443, либо порт, указанный в конфиге).

Лучшие практики и рекомендации

  • Валидация входных данных: используйте binding/валидацию в Gin и проверяйте обязательные поля.
  • Логирование: централизованное логирование (например, zap, logrus). Не выводите секреты в логи.
  • Контексты и таймауты: каждый запрос должен иметь контекст с таймаутом.
  • Используйте _id как поле для поиска в MongoDB.
  • Миграции и индексы: создавайте индексы для часто используемых полей.
  • Обработка ошибок: не возвращайте пользователю «сырой» error; формируйте понятные сообщения и коды статусов.

Безопасность и hardening

  • Храните строку подключения в защищённом хранилище (Vault, Secrets Manager или .env на этапе разработки).
  • Включите аутентификацию и авторизацию (JWT, OAuth) для эндпоинтов, которые изменяют данные.
  • Настройте CORS (если вызываете API из браузера).
  • В продакшне используйте TLS и ограничьте доступ к MongoDB по IP/ACL.

Когда такая архитектура не подходит

  • Если вам нужен сложный транзакционный контроль между коллекциями — SQL или распределённые транзакции могут подойти лучше.
  • Если требуется сложная аналитика и агрегации на больших объёмах данных — рассмотрите OLAP-решения.

Альтернативы

  • Web-фреймворки: Echo, Fiber, stdlib net/http.
  • БД: PostgreSQL (sqlx, gorm), Redis (для кэша), Cassandra для масштабирования по записи.

Ментальные модели (heuristics)

  • Keep it simple: сначала минимально рабочая API-логика, затем оптимизируйте.
  • Fail fast: проверяйте вход и возвращайте ошибку сразу.
  • Single responsibility: каждый обработчик делает одну работу (CRUD операцию).

Решение: как выбрать стек (decision tree)

flowchart TD
    A[Нужен быстрый API] --> B{Требуется ли транзакционность?}
    B -- Да --> C[Рассмотрите SQL 'Postgres']
    B -- Нет --> D{Нужна горизонтальная масштабируемость записей?}
    D -- Да --> E[MongoDB / Cassandra]
    D -- Нет --> F[MongoDB или Postgres — по предпочтению]

Чек-листы по ролям

Developer:

  • Локально работает сервер
  • Тесты покрывают базовые сценарии CRUD
  • Конфигурация вынесена в .env

DevOps:

  • Секреты вынесены в секрет-менеджер
  • Настроен CI/CD (build -> test -> deploy)
  • Мониторинг и alerting

QA:

  • Тест-кейсы для позитивных и негативных сценариев
  • Тесты на авторизацию/валидацию

Краткий SOP для деплоя

  1. Собрать бинарник (go build).
  2. Выполнить миграции/создать индексы в MongoDB.
  3. Настроить переменные окружения.
  4. Запустить сервис в контейнере/сервере.
  5. Прогнать smoke-tests.
  6. Переключить трафик.

Тест-кейсы (основные)

  • Create: POST / — валидный payload -> 201 и документ в БД.
  • Read: GET /getOne/:id — существующий id -> 200 и корректный документ.
  • Update: PUT /update/:id — существующий id и валидный payload -> 200 и изменения в БД.
  • Delete: DELETE /delete/:id — существующий id -> 200 и запись удалена.
  • Error: GET с невалидным id -> 400/404.

Критерии приёмки

  • Все CRUD эндпоинты возвращают корректные HTTP-коды.
  • Запросы с некорректными данными возвращают описанную ошибку.
  • Сервер стабильно обрабатывает базовые сценарии в течение 10 минут под нагрузкой (smoke test).

Мини-глоссарий

  • CRUD — Create, Read, Update, Delete.
  • URI — строка подключения к MongoDB.
  • Context — механизм контроля времени выполнения операций в Go.

Когда это не сработает / Ограничения

  • Невозможность обеспечить сильную согласованность между несколькими документами без транзакций.
  • При больших объёмах данных потребуется продуманная схема индексации и шардирование.

Маленькая методология разработки

  1. Определите модель данных и API контракт (OpenAPI/Swagger).
  2. Реализуйте минимальные обработчики (happy path).
  3. Добавьте валидацию и обработку ошибок.
  4. Покройте тестами.
  5. Подготовьте CI/CD и секреты.

Заключение

Вы создали базовый CRUD API на Go с использованием Gin и MongoDB и получили набор практических рекомендаций для его улучшения и подготовки к продакшену. Следующим шагом можно добавить аутентификацию, валидацию схемы через OpenAPI и логирование с централизованным сбором метрик.

Сводка

  • Создайте модуль Go и установите Gin и MongoDB драйвер.
  • Настройте подключение к MongoDB через пакет database.
  • Опишите модель, реализуйте обработчики CRUD и запускающий main.
  • Применяйте лучшие практики безопасности, тестирования и деплоя.

Спасибо за внимание — развивайте проект: добавляйте аутентификацию, пагинацию, фильтрацию и документацию API.

Поделиться: X/Twitter Facebook LinkedIn Telegram
Автор
Редакция

Похожие материалы

Включение TLS 1.0 и 1.1 в Windows 11
Безопасность

Включение TLS 1.0 и 1.1 в Windows 11

Как подключить PS4 к Wi‑Fi в гостинице
Гайды

Как подключить PS4 к Wi‑Fi в гостинице

Fix Cxuiusvc: устранение высокой загрузки ЦП
Windows

Fix Cxuiusvc: устранение высокой загрузки ЦП

Перенос Groove Music в Spotify — пошагово
Музыка

Перенос Groove Music в Spotify — пошагово

Ускорение Unity Dash в Ubuntu
Ubuntu

Ускорение Unity Dash в Ubuntu

Исправить ошибку ASUS Armoury Crate 501
Техподдержка

Исправить ошибку ASUS Armoury Crate 501