Как создать CRUD API с Gin и MongoDB от Golang

54
технологии 36.webp

Последнее обновление 03.01.2023 — Василий Иванов

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

Узнайте, как создать CRUD API в Golang с помощью HTTP-фреймворка Gin.

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

Начните работу с Golang, установив его на свой компьютер, если вы еще этого не сделали.

После установки следующим шагом будет создание корневой папки проекта на вашем компьютере и инициализация модуля Go в этом корневом каталоге.

Для этого откройте CLI, перейдите в корневую папку проекта и выполните:

go mod init module_name

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

import(package CRUD_API/package-directory-name)

Затем установите пакеты, необходимые для создания CRUD API. В этом случае используйте Gin Gonic для маршрутизации конечных точек API:

go get github.com/gin-gonic/gin

Теперь установите драйвер MongoDB для хранения данных:

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

Как подключиться Перейти к MongoDB

Все, что вам нужно, это ваш URI MongoDB для подключения Golang к базе данных. Обычно это выглядит так, если вы подключаетесь к MongoDB Atlas локально:

Mongo_URL = "mongodb://127.0.0.1:27017"

Теперь создайте новую папку в корневом каталоге вашего проекта и назовите ее databases. Создайте файл Go внутри этой папки и назовите его 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
}

Лучше всего скрывать переменные среды, такие как строка подключения к базе данных, в файле .env с помощью пакета dotenv. Это делает ваш код более переносимым и пригодится, например, при использовании экземпляра облачного кластера MongoDB.

Функция ConnectDB устанавливает соединение и возвращает новый объект клиента MongoDB.

Создать коллекцию баз данных

MongoDB хранит данные в коллекциях, которые обеспечивают интерфейс для данных базовой базы данных.

Чтобы реализовать функцию получения коллекции, начните с создания новой папки Collection в корне проекта. Теперь создайте новый файл Go, 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
}

Эта функция получает коллекцию из базы данных MongoDB. Имя базы данных в данном случае — myGoappDB, а ее коллекция — Posts.

Создайте модель базы данных

Создайте новую папку в корневом каталоге и назовите ее model. Эта папка обрабатывает вашу модель базы данных.

Создайте новый файл Go внутри этой папки и назовите его model.go. Ваша модель в данном случае представляет собой сообщение в блоге с заголовком:

package model

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

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

Создание CRUD API с помощью Go

Далее следует создание CRUD API. Чтобы начать с этого раздела, создайте новую папку в корневом каталоге вашего проекта для обработки ваших конечных точек. Назовите это маршрутами.

Создайте в этой папке отдельный файл Go для каждого действия. Например, вы можете назвать их create.go, read.go, update.go и delete.go. Вы экспортируете эти обработчики как пакет маршрутов.

Как создать конечную точку POST в Go

Начните с определения конечной точки POST для записи данных в базу данных.

Внутри route/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}})
}

Этот код начинается с импорта пользовательских модулей проекта. Затем он импортирует сторонние пакеты, включая Gin и драйвер MongoDB.

Кроме того, postCollection содержит коллекцию базы данных. Примечательно, что c.BindJSON(«post») — это экземпляр модели JSONified, который вызывает каждое поле модели как postPayload; это входит в базу данных.

Как создать конечную точку GET

Конечная точка GET в route/read.go считывает один документ из базы данных по его уникальному идентификатору. Он также начинается с импорта пользовательских и сторонних пакетов:

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})
}

Переменная postId является объявлением параметра. Он получает идентификатор объекта документа как objId.

Однако результатом является экземпляр модели базы данных, которая позже содержит возвращенный документ как res.

Как создать конечную точку PUT

Обработчик PUT в route/update.go аналогичен обработчику POST. На этот раз он обновляет существующий пост по его уникальному идентификатору объекта:

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})
}

Формат JSON экземпляра модели (сообщения) вызывает каждое поле модели из базы данных. Переменная результата использует оператор MongoDB $set для обновления требуемого документа, вызываемого его идентификатором объекта.

Условие result.MatchedCount предотвращает запуск кода, если в базе данных нет записи или переданный идентификатор недействителен.

Создание конечной точки DELETE

Конечная точка DELETE в delete.go удаляет документ на основе идентификатора объекта, переданного в качестве параметра URL:

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})
}

Этот код удаляет запись с помощью функции DeleteOne. Он также использует свойство result.DeletedCount, чтобы предотвратить запуск кода, если база данных пуста или идентификатор объекта недействителен.

Создайте файл запуска API

Наконец, создайте main.go внутри корневого каталога вашего проекта. Ваша окончательная структура проекта должна выглядеть так:

Этот файл обрабатывает выполнение маршрутизатора для каждой конечной точки:

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")
}

Этот файл является основным пакетом, который запускает другие файлы. Он начинается с импорта обработчиков маршрутов. Далее идет переменная маршрутизатора, экземпляр gin, который вызывает действия HTTP и вызывает каждую конечную точку по имени ее функции из пакета маршрутов.

Ваш проект CRUD работает на локальном хосте: 3000. Чтобы запустить сервер и протестировать CRUD API, выполните следующую команду в базовом каталоге:

go run main.go

Превратите свой проект Golang CRUD в полезный продукт

Вы успешно создали CRUD API с помощью Go; поздравляю! Хотя это небольшой проект, вы видели, что нужно для выполнения обычных HTTP-запросов в Go.

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