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`).

C
# 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.

C
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.

C
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.