REST API in Golang Project(Beginner Friendly): CRUD Operations

Sourin
3 min readOct 30, 2023

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 :)

--

--