Introduction to CRUD Operations in Go
Go is an excellent language for building robust and scalable backend APIs. In this article, we will create a CRUD (Create, Read, Update, Delete) API in Go using the powerful Gin web framework and Gorm ORM to interface with MongoDB.
We will structure the API for maintainability and code reuse. The steps will provide a detailed walkthrough to get you up and running with a production-grade CRUD Go+Gin API talking to MongoDB.
Project Architecture
For the API server codebase, we will use the following structure:
├── cmd
│ └── api
│ └── main.go (entrypoint)
├── configs
│ └── config.go (env configs)
├── internal
│ └── apis
│ └── api.go (routes)
│ └── controllers
│ └── controllers.go (request handling)
│ └── models
│ └── models.go (data models)
│ └── repositories
│ └── repos.go (data access)
├── pkg
│ └── database
│ └── database.go (db initialization)
├── tests
└── go.mod (dependencies)
This layout separates concerns into discrete folders providing a maintainable structure as the app grows.
Setup and Config
Let’s begin by bootstrapping a new Go project:
mkdir gocrudapi
cd gocrudapi
go mod init github.com/testuser/gocrudapi
Under configs
, let’s add config.go to manage environment variables:
package configs
import "os"
type Config struct {
MongoDBURL string
}
func GetConfig() *Config {
return &Config{
MongoDBURL: os.Getenv("MONGODB_URL"),
}
}
This will hold the MongoDB connection URL from environment variables that we’ll initialize later.
Model and Repository Layer
Under internal/models
, let’s define a Post model with title and content fields:
package models
type Post struct {
ID string `bson:"_id" json:"id"`
Title string `json:"title"`
Content string `json:"content"`
}
Next, under internal/repositories
let’s add CRUD methods for posts:
package repositories
import (
"context"
"github.com/testuser/gocrudapi/internal/models"
"go.mongodb.org/mongo-driver/bson"
"go.mongodb.org/mongo-driver/bson/primitive"
"go.mongodb.org/mongo-driver/mongo"
)
const dbName = "myapp"
const postsCollection = "posts"
var collection *mongo.Collection
type PostRepo interface {
GetAll(ctx context.Context) ([]models.Post, error)
GetByID(ctx context.Context, id string) (models.Post, error)
Create(ctx context.Context, post models.Post) error
Update(ctx context.Context, post models.Post) error
Delete(ctx context.Context, id string) error
}
This defines our data repository interface and methods to work with posts in MongoDB.
Database Initialization
Under pkg/database
, let’s add database.go:
package database
import (
"context"
"log"
"time"
"github.com/testuser/gocrudapi/configs"
"go.mongodb.org/mongo-driver/mongo"
"go.mongodb.org/mongo-driver/mongo/options"
)
func Initialize() *mongo.Client {
config := configs.GetConfig()
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
client, err := mongo.Connect(ctx, options.Client().ApplyURI(config.MongoDBURL))
defer cancel()
if err != nil {
log.Fatal(err)
}
return client
}
This initializes the MongoDB client to use across the app.
API Routes and Controllers
Now let’s wire up the API endpoint routes in internal/apis/api.go
:
package apis
import (
"github.com/gin-gonic/gin"
"github.com/testuser/gocrudapi/internal/controllers"
)
func RegisterRoutes(router *gin.Engine) {
posts := router.Group("/posts"){
posts.GET("/", controllers.GetAllPosts)
posts.GET("/:id", controllers.GetPostByID)
posts.POST("/", controllers.CreatePost)
posts.PUT("/:id", controllers.UpdatePost)
posts.DELETE("/:id", controllers.DeletePost)
}
}
And we’ll add the route handlers in internal/controllers/controllers.go
:
package controllers
import (
"context"
"net/http"
"github.com/gin-gonic/gin"
"github.com/testuser/gocrudapi/internal/models"
"github.com/testuser/gocrudapi/internal/repositories"
"go.mongodb.org/mongo-driver/bson/primitive"
)
var postRepo repositories.PostRepo
// GetAllPosts fetches all posts
func GetAllPosts(ctx *gin.Context) {
posts, err := postRepo.GetAll(context.TODO())
if err != nil {
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
ctx.JSON(http.StatusOK, posts)
}
// GetPostByID fetches post by id
func GetPostByID(ctx *gin.Context) {
id := ctx.Param("id")
post, err := postRepo.GetByID(context.TODO(), id)
if err != nil {
ctx.AbortWithStatus(http.StatusNotFound)
return
}
ctx.JSON(http.StatusOK, post)
}
// CreatePost creates a new post
func CreatePost(ctx *gin.Context) {
var post models.Post
if err := ctx.ShouldBindJSON(&post); err != nil {
ctx.AbortWithStatus(http.StatusBadRequest)
return
}
err := postRepo.Create(context.TODO(), post)
if err != nil {
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
ctx.JSON(http.StatusCreated, post)
}
// UpdatePost updates an existing post
func UpdatePost(ctx *gin.Context) {
id := ctx.Param("id")
var post models.Post
if err := ctx.ShouldBindJSON(&post); err != nil {
ctx.AbortWithStatus(http.StatusBadRequest)
return
}
post.ID = id
err := postRepo.Update(context.TODO(), post)
if err != nil {
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
ctx.JSON(http.StatusOK, post)
}
// DeletePost deletes a post
func DeletePost(ctx *gin.Context) {
id := ctx.Param("id")
err := postRepo.Delete(context.TODO(), id)
if err != nil {
ctx.AbortWithStatus(http.StatusInternalServerError)
return
}
ctx.Status(http.StatusNoContent)
}
Tying It Together
Finally, let’s complete main.go under cmd/api
to run the API server:
package main
import (
"os"
"github.com/gin-gonic/gin"
"github.com/joho/godotenv"
"github.com/testuser/gocrudapi/configs"
"github.com/testuser/gocrudapi/internal/apis"
"github.com/testuser/gocrudapi/pkg/database"
)
func main() {
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
mongoClient := database.Initialize()
router := gin.Default()
apis.RegisterRoutes(router)
router.Run()
}
This loads the environment, initializes MongoDB, sets up routing, and runs the API server.
With this structured foundation integrating Gin, Gorm, and MongoDB – we can build a robust production-ready CRUD Operations in Go!
Running the API
To run the API:
- Start MongoDB
- Create a .env file with the MONGODB_URL connection string
- go run cmd/api/main.go
The API will be live on http://localhost:8080!
Conclusion
In this article, we built a cleanly architected CRUD Operations in Go leveraging some of its most useful frameworks – Gin and Gorm.
Combined with MongoDB’s flexible document model, we can quickly build powerful and scalable backends.
The code examples and structure provided here serve as a great starting point for developing real-world Go web services.