We’ll go over creating a simple REST API in Go that can do CRUD (Create, Read, Update, Delete) actions in this article. The Gin framework, a quick and simple web framework for Go, will be used.
Source Code
Access the source code here: GitHub
Prerequisites:
- Go installed in your machine
- Your enthusiasm
In this tutorial we are not using any database, using struct data type for simplicity.
Setting Up the Project
Create a new directory for your project and initialise a Go module.
mkdir go-crud-api
cd go-crud-api
go mod init example.com/myapi
Dependencies
To manage dependencies, we’ll use Go modules. In your project directory, create a file named go.mod
and add the following lines to specify the “Gin” framework as a dependency.
module myapi
go 1.17
require github.com/gin-gonic/gin v1.7.4
Next, fetch the Gin package, run the following command in your shell:
go mod tidy
Creating the main.go
File
In your project directory, create a main.go
file to start building the REST API.
package main
import (
"errors"
"net/http"
"github.com/gin-gonic/gin"
)
type todo struct {
ID string `json:"id"`
Item string `json:"item"`
Completed bool `json:"completed"`
}
var todos = []todo{
{ID: "1", Item: "Getting a space suit", Completed: false},
{ID: "2", Item: "Getting a spaceship", Completed: false},
{ID: "3", Item: "Going to Mars", Completed: false},
}
func main() {
router := gin.Default()
router.GET("/todos", getTodos) // Read
router.GET("/todos/:id", getTodo) // Read
router.POST("/todos", addTodo) // Create
router.PUT("/todos/:id", updateTodo) // Update
router.DELETE("/todos/:id", deleteTodo) // Delete
router.Run(":9000")
}
Next we are gonna create the functions that will actually gonna perform CRUD operations. We will can write other functions below or above main function.
Create (POST)
In the main.go
file, we have defined a route for creating a new todo
item:
router.POST("/todos", addTodo)
Let’s implement the addTodo
function:
func addTodo(context *gin.Context) {
var newTodo = []todo{}
err := context.BindJSON(&newTodo)
if err != nil {
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
todos = append(todos, newTodo...)
context.IndentedJSON(http.StatusCreated, newTodo)
}
In this code, we’re parsing JSON data from the request body, checking for errors, appending the new todo item to our slice, and responding with the created todo. This is how we handle the POST
request to create a new todo.
Read (GET)
For reading todos, we have already defined two routes in the main.go
file. First one is for reading all the todo, second one is for getting a particular todo from the todo-list.
router.GET("/todos", getTodos)
router.GET("/todos/:id", getTodo)
Now, let’s implement the getTodos
and getTodo
functions:
func getTodos(context *gin.Context) {
context.IndentedJSON(http.StatusOK, todos)
}
func getTodo(context *gin.Context) {
id := context.Param("id")
todo, err := getTodoIndexById(id)
if err != nil {
context.IndentedJSON(http.StatusNotFound, gin.H{"message": "Todo not found"})
return
}
context.IndentedJSON(http.StatusOK, todos[todo])
}
func getTodoIndexById(id string) (int, error) {
for i, t := range todos {
if t.ID == id {
return i, nil
}
}
return -1, errors.New("Todo not found")
}
In the getTodos
function, we respond with a JSON array of all todos, and in the getTodo
function, we retrieve a single todo by its ID and respond accordingly.
Update (PUT)
For updating a todo, we had already defined the route in the main.go
file
router.PUT("/todos/:id", updateTodo)
Here’s the updateTodo
function:
func updateTodo(context *gin.Context) {
id := context.Param("id")
todo, err := getTodoIndexById(id)
if err != nil {
context.JSON(http.StatusNotFound, gin.H{"message": "Todo not found"})
return
}
if err := context.BindJSON(todo); err != nil {
context.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
context.IndentedJSON(http.StatusOK, todo)
}
In this code, we first find the todo item by its ID, update it with the data from the request body, and respond with the updated todo.
Delete (DELETE)
For deleting a todo, we had already defined the route in the main.go
file:
router.DELETE("/todos/:id", deleteTodo)
Now, let’s implement the deleteTodo
function:
func deleteTodo(context *gin.Context) {
id := context.Param("id")
index, err := getTodoIndexById(id)
if err != nil {
context.JSON(http.StatusNotFound, gin.H{"message": "Todo not found"})
return
}
todos = append(todos[:index], todos[index+1:]...)
context.JSON(http.StatusOK, gin.H{"message": "Todo deleted"})
}
Running the Code:
Now, we have to run the project.
For that run go build
on your shell and then run ./myapi
. It will run your code on port 9000 on your local machine.
You can test the code using Postman or Thunderclient(VS Code extension).
If you face any probelm, hit me up here :)