Create Rest Api With Go Gin GORM and Postgres

In this post we'll create an API using Go, Gin and GORM, that will allow us to save and fetch todo items from a Postgres database.

Project Setup

To start the project setup, create a directory (folder) with the name of your project. Here I'll create a directory named todo-go-backend.

Let's initialize Go module in our project so that we can install libraries using go mod. Run the following command in the project directory:

go mod init your-module-name

Most popular convention for naming the go modules is this, Let's assume you are going to push your project on GitHub then module name would look something like this github.com/[your-username]/[project-name]. So here I am going to name the module github.com/dvlpr-shivendra/todo-go-backend. So I would initialize the module like this:

go mod init github.com/dvlpr-shivendra/todo-go-backend

Now inside the project directory create a file named main.go and paste the following code in that file.

package main

func main() {
    //
}

Gin Setup

First of all let's install Gin. Run the following command in the project's root directory:

go get -u github.com/gin-gonic/gin

Now import the Gin inside the main.go. Add following code below the package main

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

Inside the main function add this code to initialize the router:

r := gin.Default()

r.Run() // listen and serve on localhost:8080

Now we are ready to add end points to the router. Let's add an endpoint to add todo items. Add this code below the r := gin.Default()

r.POST("/todos", handleAddTodo)

Now ff we hit POST http://localhost:8080/todos in the browser then Gin will execute handleAddTodo function, So let's add that above the main function

func handleAddTodo(c *gin.Context) {
    c.JSON(200, gin.H{
        "message": "Here we'll add a new todo",
    })
}

Every Gin route handler receives *gin.Context in the argument. Using gin.Context we can interact with the request and send the response back.

Right now the complete main.go would look like this

package main

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

func handleAddTodo(c *gin.Context) {
    c.JSON(200, gin.H{
        "message": "Here we'll add a new todo",
    })
}

func main() {
    r := gin.Default()

    r.POST("/todos", handleAddTodo)

    r.Run() // listen and serve on 0.0.0.0:8080 (for windows "localhost:8080")
}

Let's test our changes so far. Execute go run main.go in the terminal to start the application.

Now open some API client and make a POST request at http://localhost:8080/todos and you should get the following response:

{
    "message": "Here we'll add a new todo"
}

Now we are ready to install and use GORM.

GORM Setup

To install GORM run the following command in the terminal:

go get -u gorm.io/gorm

Now let's install Postgres driver for GORM

go get -u gorm.io/driver/postgres

We are now ready to connect to the Postgres database. Let's import GORM and the Postgres driver in the import section. After importing the import section should look like this:

"github.com/gin-gonic/gin"

"gorm.io/gorm"
"gorm.io/driver/postgres"

Now let's add a global variable to store the gorm.DB instance, which we'll use to interact with the database. We can add the variable below the import section:

var db *gorm.DB

We need to define the Todo struct. We'll use this struct to create todo table and to create todo items. Add following code below the import section

type Todo struct {
    gorm.Model
    Title     string `json:"title"`
    Completed string `json:"completed" gorm:"default:false"`
}

below the db variable let's add a function to connect to the database and store the db instance in the global variable db which we just created.

func initDb() {
    var err error

    dsn := "host=localhost user=postgres password=postgres dbname=todo-app port=5432 sslmode=disable"

    db, err = gorm.Open(postgres.Open(dsn), &gorm.Config{})

    if err != nil {
        panic(err)
    }

    db.AutoMigrate(&Todo{}) // create table if not exist
}

Add the details in the dsn string as per your postgres installation.

Updating handleAddTodo to save Todos in database using Gorm

Now let's update the handleAddTodo to save the todo item in the database using GORM

func handleAddTodo(c *gin.Context) {
    var todo Todo
    
    c.BindJSON(&todo) // bind request body to struct

    db.Create(&todo) // insert data to database

    c.JSON(200, todo) // send response
}

Now make a POST request at http://localhost:8080/todos . The body of the request should contain following JSON

{
    "title": "Write a blog"
}

If everything went as expected then todo should get inserted in a table name todos and response body should contain the newly created todo.

Endpoint to fetch Todo list

Let's add the endpoint to fetch todo list. To define the endpoint add the following code above the endpoint to save todo.

r.GET("/todos", handleFetchTodos)

Now let's define handleFetchTodos below the handleSaveTodo function

func handleFetchTodos(c *gin.Context) {
    var todos []Todo
    db.Find(&todos)
    c.JSON(200, todos)
}

Now make a get request to GET http://localhost:8080/todos and you should get the array todo in response.