English | 简体中文
A production-ready Go web application boilerplate built with Gin + GORM, featuring best practices and clean architecture.
- 🚀 Complete Project Structure - Clean layered architecture (Controller, Service, Model, Router)
- 🔐 JWT Authentication - Complete user authentication system
- ⚙️ Multi-Environment Config - Support for development, production, and more (Viper-based)
- 🗄️ Database ORM - GORM with auto-migration support
- 🔒 Password Encryption - Bcrypt password hashing
- 📝 Logging Middleware - Request logging
- 🌐 CORS Support - Cross-Origin Resource Sharing middleware
- 📦 Unified Response Format - Standardized API response structure
- 🎨 Startup Banner - Spring Boot-style startup banner
- 🧪 API Test Scripts - Multiple testing script options
- 🧩 Plugin System - Modular plugin architecture for extensibility
gin-boilerplate/
├── config/ # Configuration files
│ ├── banner.txt # Startup banner
│ ├── config.go # Configuration loading logic
│ ├── default.yaml # Default configuration
│ ├── development.yaml # Development environment config
│ └── production.yaml # Production environment config
├── controllers/ # Controller layer
│ ├── auth_controller.go # Authentication controller
│ └── user_controller.go # User controller
├── database/ # Database connection
│ └── database.go
├── middleware/ # Middlewares
│ ├── auth.go # JWT authentication middleware
│ ├── cors.go # CORS middleware
│ └── logger.go # Logging middleware
├── models/ # Data models
│ ├── base.go # Base model
│ └── user.go # User model
├── plugins/ # Plugin system and implementations
│ ├── environment.go # Plugin runtime environment
│ ├── plugin.go # Plugin interface definition
│ ├── registry.go # Plugin registry
│ └── guest/ # Guest plugin example
│ ├── controllers/
│ ├── models/
│ ├── services/
│ └── guest_plugin.go
├── router/ # Router layer
│ └── router.go
├── scripts/ # Scripts
│ ├── api-test.http # HTTP test file
│ ├── api-test.sh # Bash test script
│ └── init.sql # Database initialization script
├── services/ # Business logic layer
│ ├── auth_service.go # Authentication service
│ └── user_service.go # User service
├── utils/ # Utilities
│ ├── banner.go # Banner utility
│ ├── jwt.go # JWT utility
│ └── response.go # Response utility
├── .gitignore
├── go.mod
├── main.go # Application entry point
└── README.md
- Docker
- MySQL (external or separate container)
- Clone the project
git clone <repository-url>
cd gin-boilerplate
- Configure production settings
Edit config/production.yaml
to match your database configuration:
database:
host: "your-mysql-host"
port: "3306"
user: "your-db-user"
password: "your-db-password"
dbname: "gin_boilerplate_prod"
- Build Docker image
docker build -t gin-boilerplate:latest .
- Run container
docker run -d \
--name gin-boilerplate \
-p 8080:8080 \
-v $(pwd)/config:/root/config \
gin-boilerplate:latest
- Check logs
docker logs -f gin-boilerplate
- Stop container
docker stop gin-boilerplate
docker rm gin-boilerplate
The service runs on http://localhost:8080
by default.
- Go 1.19+
- MySQL 5.7+
- Clone the Project
git clone <repository-url>
cd gin-boilerplate
- Install Dependencies
go mod tidy
- Database Setup
Initialize Database
mysql -u root -p < scripts/init.sql
Configure Database Connection
Edit config/development.yaml
:
database:
host: "localhost"
port: "3306"
user: "root"
password: "your_password"
dbname: "gin_boilerplate_dev"
jwt:
secret: "your-secret-key"
expire_time: 72
- Run the Application
Development Environment
go run main.go
# Or specify environment
go run main.go -e development
Production Environment
go run main.go -e production
The service runs on http://localhost:8080
by default.
- Multi-stage build: Minimizes final image size
- Alpine-based: Lightweight and secure
- Production optimized: CGO disabled for static binary
This application is designed to run behind a reverse proxy (Nginx, Traefik, etc.). The reverse proxy should handle:
- SSL/TLS termination
- Load balancing
- Rate limiting
- Static file serving (if needed)
Example Nginx configuration:
upstream gin_backend {
server localhost:8080;
}
server {
listen 80;
server_name your-domain.com;
location / {
proxy_pass http://gin_backend;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Before deploying to production:
- Change the JWT secret in
config/production.yaml
- Configure external MySQL database
- Set up proper logging and monitoring
- Configure firewall rules
- Use reverse proxy for SSL/TLS
- Set up automated backups for database
POST /api/v1/auth/register
Content-Type: application/json
{
"username": "testuser",
"email": "test@example.com",
"password": "password123",
"full_name": "Test User"
}
Response:
{
"success": true,
"code": 200,
"message": "Success",
"data": {
"user": {
"id": 1,
"username": "testuser",
"email": "test@example.com",
"full_name": "Test User",
"created_at": "2024-01-01T00:00:00Z"
}
}
}
POST /api/v1/auth/login
Content-Type: application/json
{
"username": "testuser",
"password": "password123"
}
Response:
{
"success": true,
"code": 200,
"message": "Success",
"data": {
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
"user": {
"id": 1,
"username": "testuser",
"email": "test@example.com",
"full_name": "Test User"
}
}
}
All user-related endpoints require a token in the header:
Authorization: Bearer {your_token}
GET /api/v1/me
Authorization: Bearer {token}
PUT /api/v1/me
Authorization: Bearer {token}
Content-Type: application/json
{
"full_name": "Updated Name",
"email": "newemail@example.com"
}
PUT /api/v1/me
Authorization: Bearer {token}
Content-Type: application/json
{
"password": "newpassword123"
}
GET /api/v1/health
Response:
{
"status": "ok",
"message": "Service is running"
}
The project provides multiple API testing scripts:
Use VS Code REST Client extension or IntelliJ IDEA HTTP Client:
# Open scripts/api-test.http file
# Click "Send Request" to execute tests
For Linux/Mac users:
chmod +x scripts/api-test.sh
./scripts/api-test.sh
For Windows users (Git Bash):
bash scripts/api-test.sh
config/default.yaml
- Base configuration (shared across all environments)config/{env}.yaml
- Environment-specific configuration (overrides defaults)
# Server configuration
server:
port: "8080" # Server port
mode: "debug" # Running mode: debug, release, test
# Database configuration
database:
host: "localhost" # Database host
port: "3306" # Database port
user: "root" # Database username
password: "" # Database password
dbname: "gin_boilerplate" # Database name
# JWT configuration
jwt:
secret: "your-secret-key" # JWT secret key (MUST change in production)
expire_time: 24 # Token validity period (hours)
Edit the config/banner.txt
file to customize your startup banner.
- Create Model (
models/
)
type Product struct {
BaseModel
Name string `gorm:"not null" json:"name"`
Price float64 `json:"price"`
}
- Create Service (
services/
)
type ProductService struct{}
func (s *ProductService) CreateProduct(product *models.Product) error {
return database.GetDB().Create(product).Error
}
- Create Controller (
controllers/
)
type ProductController struct {
productService *services.ProductService
}
func (c *ProductController) CreateProduct(ctx *gin.Context) {
// Handle request
}
- Register Routes (
router/router.go
)
productController := controllers.NewProductController()
productRoutes := authenticated.Group("/products")
{
productRoutes.POST("", productController.CreateProduct)
productRoutes.GET("", productController.GetAllProducts)
}
// Global middleware
r.Use(middleware.Logger())
// Route group middleware
authenticated := v1.Group("")
authenticated.Use(middleware.JWTAuth())
Add auto-migration for new models in main.go
:
database.GetDB().AutoMigrate(
&models.User{},
&models.Product{}, // New model
)
The plugin system allows you to extend the application with modular features without modifying the core codebase.
The plugin system provides:
- Plugin Interface: Define plugin contract with
Register()
andRouterPath()
methods - Plugin Registry: Auto-register plugins at startup using
init()
functions - Plugin Environment: Share dependencies (database, etc.) with plugins
- Isolated Routes: Each plugin gets its own route group under
/api/v1/plugin/{plugin-name}
1. Create Plugin Structure
plugins/
└── myplugin/
├── controllers/
│ └── myplugin_controller.go
├── models/
│ └── myplugin_model.go
├── services/
│ └── myplugin_service.go
└── myplugin_plugin.go
2. Define Plugin Model (models/myplugin_model.go
)
package models
import "gin-boilerplate/models"
type MyPluginData struct {
models.BaseModel
Name string `gorm:"not null" json:"name"`
}
3. Create Plugin Controller (controllers/myplugin_controller.go
)
package controllers
import "github.com/gin-gonic/gin"
type MyPluginController struct{}
func NewMyPluginController() *MyPluginController {
return &MyPluginController{}
}
func (c *MyPluginController) Hello(ctx *gin.Context) {
ctx.JSON(200, gin.H{"message": "Hello from MyPlugin"})
}
4. Implement Plugin Interface (myplugin_plugin.go
)
package myplugin
import (
"gin-boilerplate/plugins"
"gin-boilerplate/plugins/myplugin/controllers"
"gin-boilerplate/plugins/myplugin/models"
"github.com/gin-gonic/gin"
"gorm.io/gorm"
)
// Auto-register plugin on import
func init() {
plugins.Register("myplugin", NewMyPlugin)
}
type MyPlugin struct {
db *gorm.DB
}
func NewMyPlugin(env *plugins.PluginEnvironment) plugins.Plugin {
return &MyPlugin{db: env.DB}
}
func (p *MyPlugin) RouterPath() string {
return "/myplugin"
}
func (p *MyPlugin) Register(group *gin.RouterGroup) error {
// Auto-migrate plugin tables
if err := p.db.AutoMigrate(&models.MyPluginData{}); err != nil {
return err
}
// Register routes
controller := controllers.NewMyPluginController()
group.GET("/hello", controller.Hello)
return nil
}
5. Import Plugin in main.go
import (
_ "gin-boilerplate/plugins/myplugin" // Auto-register plugin
)
All plugins are automatically mounted under /api/v1/plugin/
:
- Guest plugin:
http://localhost:8080/api/v1/plugin/guest/*
- Your plugin:
http://localhost:8080/api/v1/plugin/myplugin/*
The project includes a guest plugin example at plugins/guest/
that demonstrates:
- Plugin registration and initialization
- Route setup under
/api/v1/plugin/guest
- Database model auto-migration
- Controller implementation
Test the Guest Plugin:
curl -X POST http://localhost:8080/api/v1/plugin/guest/login
- Self-contained: Keep plugin code isolated in its own directory
- Database migrations: Use
AutoMigrate()
in plugin'sRegister()
method - Naming convention: Use lowercase for plugin names and route paths
- Error handling: Return errors from
Register()
for proper initialization - Dependencies: Access shared resources through
PluginEnvironment
- Change JWT Secret: Use a strong secret key in production
- HTTPS: Use HTTPS in production environments
- Database Password: Don't commit production config files to Git
- Input Validation: Use Gin's binding for user input validation
- Rate Limiting: Add API rate limiting middleware as needed
- Gin - Web framework
- GORM - ORM library
- Viper - Configuration management
- JWT - JWT authentication
- bcrypt - Password encryption
- Add unit tests
- Add API documentation (Swagger)
- Add Docker support
- Add rate limiting middleware
- Add cache support (Redis)
- Add file logging
MIT License
Issues and Pull Requests are welcome!
Happy Coding! 🎉