How to Build an Enterprise‑Grade API with Gin and GORM

This tutorial walks through creating a complete Go backend service using Gin and GORM, covering project setup, layered architecture, database initialization, user model definition, JWT‑based registration and login, pagination, middleware, routing, and deployment options, while also suggesting extensible features such as RBAC, Swagger, Redis caching, Docker, and micro‑service frameworks.

Golang Shines
Golang Shines
Golang Shines
How to Build an Enterprise‑Grade API with Gin and GORM

Project Goal

Implement a user‑management API with features including registration, JWT login, paginated user listing, and placeholders for permission management.

Technology Stack

Gin (web framework) + GORM (ORM) + Viper (configuration) + Zap (logging) + golang‑jwt (JWT handling) + MySQL driver.

Project Structure

go-gin-api/
├── config/        # configuration
├── controller/    # controller layer
├── middleware/    # middleware
├── model/         # data models
├── router/        # route registration
├── service/       # business logic
├── utils/         # utility functions
├── main.go        # program entry
└── go.mod

The author strongly recommends a layered architecture (Controller → Service → Model) to facilitate unit testing and future extensions.

Environment Initialization

Initialize Go module: go mod init go-gin-api Install dependencies:

go get -u github.com/gin-gonic/gin
go get -u gorm.io/gorm
go get -u gorm.io/driver/mysql
go get -u github.com/spf13/viper
go get -u go.uber.org/zap
go get -u github.com/golang-jwt/jwt/v5

Database Configuration & GORM Initialization

// config/database.go
package config

import (
    "gorm.io/driver/mysql"
    "gorm.io/gorm"
    "log"
)

var DB *gorm.DB

func InitDB() {
    dsn := "root:password@tcp(127.0.0.1:3306)/test_db?charset=utf8mb4&parseTime=True&loc=Local"
    var err error
    DB, err = gorm.Open(mysql.Open(dsn), &gorm.Config{})
    if err != nil {
        log.Fatal("Failed to connect to database:", err)
    }
}

Data Model Definition (User)

// model/user.go
package model

import "gorm.io/gorm"

type User struct {
    gorm.Model
    Username string `gorm:"unique"`
    Password string
    Email    string
}

Registration & Login Endpoints (JWT Authentication)

Registration

// controller/user.go
func Register(c *gin.Context) {
    var user model.User
    if err := c.ShouldBindJSON(&user); err != nil {
        c.JSON(400, gin.H{"msg": "Invalid parameters"})
        return
    }
    // Password hashing can be done with bcrypt
    if err := config.DB.Create(&user).Error; err != nil {
        c.JSON(500, gin.H{"msg": "Registration failed"})
        return
    }
    c.JSON(200, gin.H{"msg": "Registration successful"})
}

Login (Issue JWT)

func Login(c *gin.Context) {
    var input model.User
    var user model.User
    if err := c.ShouldBindJSON(&input); err != nil {
        c.JSON(400, gin.H{"msg": "Invalid parameters"})
        return
    }
    config.DB.Where("username = ? AND password = ?", input.Username, input.Password).First(&user)
    if user.ID == 0 {
        c.JSON(401, gin.H{"msg": "Incorrect username or password"})
        return
    }
    token, _ := utils.GenerateToken(user.Username) // custom JWT generation
    c.JSON(200, gin.H{"token": token})
}

Paginated Query Example

func ListUsers(c *gin.Context) {
    var users []model.User
    page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
    pageSize := 10
    offset := (page - 1) * pageSize
    config.DB.Offset(offset).Limit(pageSize).Find(&users)
    c.JSON(200, users)
}

JWT Authentication Middleware

func JWTAuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        tokenStr := c.GetHeader("Authorization")
        claims, err := utils.ParseToken(tokenStr)
        if err != nil {
            c.AbortWithStatusJSON(401, gin.H{"msg": "Unauthenticated or invalid token"})
            return
        }
        c.Set("username", claims.Username)
        c.Next()
    }
}

Router Registration

// router/router.go
func InitRouter() *gin.Engine {
    r := gin.Default()
    r.POST("/register", controller.Register)
    r.POST("/login", controller.Login)

    auth := r.Group("/api", middleware.JWTAuthMiddleware())
    {
        auth.GET("/users", controller.ListUsers)
        // other CRUD endpoints
    }
    return r
}

Start the Application

// main.go
func main() {
    config.InitDB()
    r := router.InitRouter()
    r.Run(":8080")
}

Running the Service

Use tools like Postman or curl to test the endpoints:

POST /register – user registration

POST /login – obtain JWT token

GET /api/users – retrieve paginated user list with JWT authentication

Potential Extensions

Integrate Casbin for RBAC permission management

Add Swagger to generate API documentation

Use Redis for caching and rate limiting

Package the application into a Docker image and deploy to Kubernetes

Adopt go‑zero to build a micro‑service architecture

What You Will Gain

Complete development workflow for Gin + GORM

Understanding of layered architecture for maintainable code

Practical experience with JWT authentication, pagination, and hot reload

A ready‑to‑extend Golang backend project template

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

backend developmentGoAPIJWTGORMGin
Golang Shines
Written by

Golang Shines

We share daily the latest Golang technical articles, practical resources, language news, tutorials, and real-world projects to help everyone learn and improve.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.