Build a REST API in Go with Gin and PostgreSQL
Go (**Golang**) has become a dominant language for building high-performance, concurrent, and low-latency microservices and backend APIs. Combined with the **Gin** web framework—known for its blazing-fast routing and martini-like API—and **PostgreSQL** for robust relational data persistence, Go provides an exceptional developer experience for scalable backend architectures.
This comprehensive guide walks through setting up a modular Go project, configuring database connections using `database/sql` with PostgreSQL, writing CRUD endpoints in Gin, and structuring middleware.
1. Project Initialization and Dependencies
Initialize a new Go module and install the Gin framework along with the official PostgreSQL driver (`pgx`).
# Initialize Go module
mkdir go-gin-rest-api && cd go-gin-rest-api
go mod init github.com/your-username/go-gin-rest-api
# Install Gin and PostgreSQL driver
go get -u github.com/gin-gonic/gin
go get -u github.com/jackc/pgx/v5/stdlib
go get -u github.com/joho/godotenv
2. Configuring PostgreSQL Connection Pool
Establish a secure connection pool to PostgreSQL using Go's standard `database/sql` package coupled with the high-performance `pgx` driver.
package database
import (
"database/sql"
"fmt"
"log"
_ "github.com/jackc/pgx/v5/stdlib"
)
var DB *sql.DB
func ConnectPostgres(dsn string) error {
var err error
DB, err = sql.Open("pgx", dsn)
if err != nil {
return fmt.Errorf("error connecting to database: %w", err)
}
if err = DB.Ping(); err != nil {
return fmt.Errorf("database ping failed: %w", err)
}
log.Println("Successfully connected to PostgreSQL database!")
return nil;
}
3. Implementing Gin Router and CRUD Handlers
Create your main application entry point (`main.go`), setting up Gin routes for creating, reading, updating, and deleting records.
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
type Todo struct {
ID int `json:"id"`
Title string `json:"title" binding:"required"`
IsCompleted bool `json:"is_completed"`
}
func main() {
r := gin.Default()
// Health check endpoint
r.GET("/health", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"status": "healthy"})
});
// CRUD Group
api := r.Group("/api/v1")
{
api.GET("/todos", getTodos);
api.POST("/todos", createTodo);
}
r.Run(":8080");
}
func getTodos(c *gin.Context) {
// Mock response for illustration
todos := []Todo{
{ID: 1, Title: "Learn Go and Gin", IsCompleted: false},
{ID: 2, Title: "Build a PostgreSQL API", IsCompleted: true},
}
c.JSON(http.StatusOK, todos);
}
func createTodo(c *gin.Context) {
var newTodo Todo
if err := c.ShouldBindJSON(&newTodo); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
newTodo.ID = 3 // Assigned by DB in real app
c.JSON(http.StatusCreated, newTodo);
}
Conclusion
By combining **Go**, the **Gin** framework, and **PostgreSQL**, developers can architect lightning-fast, highly concurrent, and maintainable REST APIs suited for enterprise workloads.