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.
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() {
//
}
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.
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.
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.
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.