Skip to main content

Gin

Gin (https://gin-gonic.com/) is an HTTP Web framework written in the Go programming language. It features an API similar to Martini's but performs 40 times faster. Gin utilizes a custom version of HttpRouter, therefore it not only delivers extremely fast routing but also provides middleware support.

This guide describes how to deploy Gin applications on CloudBase HTTP cloud functions.

For sample source code, see: cloudrun-gin

Prerequisites

Before you begin, ensure that you have:

  • Installed Go 1.23 or a later version
  • Have a Tencent Cloud account and have activated the CloudBase service
  • Have a basic knowledge of the Go programming language and the Gin framework development

Step 1: Create a Gin Application

💡 Note: If you already have a Gin application, you can skip this step.

Create the project directory

mkdir cloudrun-gin
cd cloudrun-gin

Initialize the Go module

go mod init cloudrun-gin
go get -u github.com/gin-gonic/gin

Create the main application file

Create the main.go file in the cloudrun-gin directory:

package main

import (
"net/http"
"os"

"github.com/gin-gonic/gin"
)

func main() {
// Set the Gin mode
if os.Getenv("GIN_MODE") == "" {
gin.SetMode(gin.ReleaseMode)
}

router := gin.Default()

// Basic routes
router.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Welcome to the Gin CloudBase application!",
"status": "running",
})
})

// Health Check
router.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "healthy",
"framework": "Gin",
"go_version": "1.19+",
"gin_version": gin.Version,
})
})

// Get the port, supporting environment variables
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}

// Start up the server
router.Run(":" + port)
}

Test the application locally

Start up the application:

go run main.go

Open your browser and visit http://localhost:8080, you should see a JSON response.

Step 2: Add API routes

Let's create a RESTful API to demonstrate the features of Gin.

Create User Model

Create the models directory in the project root directory and create the user.go file:

package models

type User struct {
ID int `json:"id"`
Name string `json:"name"`
Email string `json:"email"`
}

type ApiResponse struct {
Success bool `json:"success"`
Message string `json:"message"`
Data interface{} `json:"data,omitempty"`
}

Create User Controller

Create the controllers directory in the project root directory and create the user.go file:

package controllers

import (
"net/http"
"strconv"
"sync"

"cloudrun-gin/models"

"github.com/gin-gonic/gin"
)

var (
users []models.User
usersMu sync.RWMutex
nextID = 1
)

func init() {
// Initialize test data
users = []models.User{
{ID: 1, Name: "Zhang San", Email: "zhangsan@example.com"},
{ID: 2, Name: "Li Si", Email: "lisi@example.com"},
{ID: 3, Name: "Wang Wu", Email: "wangwu@example.com"},
}
nextID = 4
}

// GetUsers obtains the user list
func GetUsers(c *gin.Context) {
page, _ := strconv.Atoi(c.DefaultQuery("page", "1"))
limit, _ := strconv.Atoi(c.DefaultQuery("limit", "10"))

usersMu.RLock()
defer usersMu.RUnlock()

startIndex := (page - 1) * limit
endIndex := startIndex + limit

if startIndex >= len(users) {
c.JSON(http.StatusOK, models.ApiResponse{
Success: true,
Message: "Obtained successfully",
Data: []models.User{},
})
return
}

if endIndex > len(users) {
endIndex = len(users)
}

paginatedUsers := users[startIndex:endIndex]

c.JSON(http.StatusOK, models.ApiResponse{
Success: true,
Message: "Obtained successfully",
Data: gin.H{
"total": len(users),
"page": page,
"limit": limit,
"items": paginatedUsers,
},
})
}

// GetUser Obtains the user by ID
func GetUser(c *gin.Context) {
id, err := strconv.Atoi(c.Param("id"))
if err != nil {
c.JSON(http.StatusBadRequest, models.ApiResponse{
Success: false,
Message: "Invalid user ID",
})
return
}

usersMu.RLock()
defer usersMu.RUnlock()

for _, user := range users {
if user.ID == id {
c.JSON(http.StatusOK, models.ApiResponse{
Success: true,
Message: "Obtained successfully",
Data: user,
})
return
}
}

c.JSON(http.StatusNotFound, models.ApiResponse{
Success: false,
Message: "User does not exist",
})
}

// CreateUser Creates a user
func CreateUser(c *gin.Context) {
var newUser models.User
if err := c.ShouldBindJSON(&newUser); err != nil {
c.JSON(http.StatusBadRequest, models.ApiResponse{
Success: false,
Message: "Request parameter error: " + err.Error(),
})
return
}

if newUser.Name == "" || newUser.Email == "" {
c.JSON(http.StatusBadRequest, models.ApiResponse{
Success: false,
Message: "Name and email cannot be empty",
})
return
}

usersMu.Lock()
newUser.ID = nextID
nextID++
users = append(users, newUser)
usersMu.Unlock()

c.JSON(http.StatusCreated, models.ApiResponse{
Success: true,
Message: "Created successfully",
Data: newUser,
})
}

Update the main application file

Update the main.go file to add routes and middleware:

package main

import (
"fmt"
"net/http"
"os"
"time"

"cloudrun-gin/controllers"

"github.com/gin-gonic/gin"
)

func main() {
// Set the Gin mode
if os.Getenv("GIN_MODE") == "" {
gin.SetMode(gin.ReleaseMode)
}

router := gin.Default()

// Add CORS middleware
router.Use(func(c *gin.Context) {
c.Header("Access-Control-Allow-Origin", "*")
c.Header("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
c.Header("Access-Control-Allow-Headers", "Content-Type, Authorization")

if c.Request.Method == "OPTIONS" {
c.AbortWithStatus(http.StatusNoContent)
return
}

c.Next()
})

// Add logging middleware
router.Use(gin.LoggerWithFormatter(func(param gin.LogFormatterParams) string {
return fmt.Sprintf("%s - [%s] \"%s %s %s %d %s \"%s\" %s\"\n",
param.ClientIP,
param.TimeStamp.Format(time.RFC1123),
param.Method,
param.Path,
param.Request.Proto,
param.StatusCode,
param.Latency,
param.Request.UserAgent(),
param.ErrorMessage,
)
}))

// Basic routes
router.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"message": "Welcome to the Gin CloudBase application!",
"status": "running",
})
})

// Health Check
router.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{
"status": "healthy",
"timestamp": time.Now().Format(time.RFC3339),
"framework": "Gin",
"go_version": "1.19+",
"gin_version": gin.Version,
})
})

// API router group
api := router.Group("/api")
{
users := api.Group("/users")
{
users.GET("", controllers.GetUsers)
users.GET("/:id", controllers.GetUser)
users.POST("", controllers.CreateUser)
}
}

// Get the port, supporting environment variables
port := os.Getenv("PORT")
if port == "" {
port = "8080"
}

// Start up the server
router.Run(":" + port)
}

Step 3: Local Testing

Update dependencies

go mod tidy

Starting Up the Application

go run main.go

Testing API Endpoints

# Test Health Check
curl http://localhost:8080/health

# Test Homepage
curl http://localhost:8080/

# Test User List
curl http://localhost:8080/api/users

# Test Pagination
curl "http://localhost:8080/api/users?page=1&limit=2"

# Test Obtaining a Single User
curl http://localhost:8080/api/users/1

# Test Creating a User
curl -X POST http://localhost:8080/api/users \
-H "Content-Type: application/json" \
-d '{"name":"New User","email":"newuser@example.com"}'

Step 4: Prepare Deployment Files

1. Create the Startup Script

💡 Note:

  • When creating the scf_bootstrap file on windows, it is recommended to use
  • When creating the scf_bootstrap file using vscode on windows, deploying to an HTTP cloud function may result in an error: scf_bootstrap file does not exist
  • This error occurs because the script file contains Windows-style carriage returns (^M), causing Linux to fail to recognize the interpreter path correctly. This is a common issue in WSL

Create the scf_bootstrap file (with no extension):

#!/bin/bash
export PORT=9000
export GIN_MODE=release
./main

Grant execute permissions to the startup script:

chmod +x scf_bootstrap

2. Compile the Application

Compile the Go application into a Linux binary:

# Cross-compile for Linux 64-bit
GOOS=linux GOARCH=amd64 go build -o main .

3. Project Structure

cloudrun-gin/
├── controllers/
│ └── user.go
├── models/
│ └── user.go
├── main.go
├── go.mod
├── go.sum
├── main # compiled binary file
└── scf_bootstrap # 🔑 Cloud function startup script

💡 Note:

  • scf_bootstrap is the startup script for CloudBase cloud functions
  • Compile the Go application into a Linux binary
  • Set the PORT=9000 environment variable to ensure the application listens on the correct port
  • Set GIN_MODE=release to optimize performance

Step 5: Deploy to CloudBase

Deploy via the console

  1. Log in to the CloudBase console
  2. Select your environment and go to the cloud function page
  3. Click "New Cloud Function"
  4. Select "HTTP cloud function"
  5. Fill in the function name (such as: gin-app)
  6. Select runtime: Go 1.x (or other supported versions)
  7. Select submit method: Local code package upload
  8. Upload the compiled binary files and the scf_bootstrap file
  9. Automatic dependency installation: Disable (Not required for Go applications)
  10. Click the "Create" button and wait for deployment to complete

Package and deploy

If you need to package manually:

# Compile the Application
GOOS=linux GOARCH=amd64 go build -o main .

# Create deployment package
zip -j gin-app.zip main scf_bootstrap

Step 6: Access Your Application

After successful deployment, you can refer to Accessing Cloud Functions via HTTP to set up custom domain access to the

Example Request

# Health Check
curl https://your-app-url/health

# Obtain User List
curl https://your-app-url/api/users

# Pagination Query
curl "https://your-app-url/api/users?page=1&limit=2"

# Create New User
curl -X POST https://your-app-url/api/users \
-H "Content-Type: application/json" \
-d '{"name":"Test User","email":"test@example.com"}'

Frequently Asked Questions

Q: Why must the HTTP cloud function use port 9000?

A: CloudBase HTTP cloud functions require the application to listen on port 9000, which is the standard configuration of the platform. The application controls the port via the environment variable PORT=9000, and uses port 8080 by default during local development.

Q: How to perform cross-compilation for Go applications?

A: Use the following command to perform cross-compilation:

GOOS=linux GOARCH=amd64 go build -o main .

Q: How to optimize the cold startup time of Go applications?

A:

  • Reduce the number of dependency packages
  • Use gin.ReleaseMode mode
  • Avoid performing duplicate initialization operations during startup
  • Configure memory appropriately

Q: What is the purpose of the scf_bootstrap file?

A: scf_bootstrap is the startup script for cloud functions, used to set environment variables and start up the compiled binary file.

Next steps