Search

Suggested keywords:
  • Java
  • Docker
  • Git
  • React
  • NextJs
  • Spring boot
  • Laravel

Building CRUD REST API using Go

  • Share this:

post-title

Go, often referred to as Golang, is a statically typed, compiled programming language developed by Google. It was created with the primary goal of being simple, efficient, and easy to use. Go is designed to address the challenges of modern software development, and it has gained popularity for various reasons, including its simplicity, performance, and strong support for concurrency.

Why Use Go in Backend Development?

1. Concurrency and Efficiency:

One of Go's standout features is its built-in support for concurrency. The language provides goroutines, which are lightweight threads managed by the Go runtime. Goroutines make it easy to write concurrent programs, enabling efficient use of resources. Go's concurrency model is designed to be straightforward, making it easier for developers to write concurrent code without the complexities of traditional thread-based approaches.

2. Fast Compilation:

Go's compilation is incredibly fast. This is crucial for development workflows, especially in large projects where quick iteration is essential. The language's compilation speed contributes to a more efficient development cycle and faster deployment times.

3. Strong Standard Library:

Go comes with a comprehensive standard library that includes packages for handling HTTP, cryptography, file I/O, and much more. This rich set of libraries simplifies the development process by providing pre-built functionality for common tasks, reducing the need for external dependencies.

4. Static Typing and Safety:

Go is statically typed, which means that type checking is performed at compile time. This can catch many errors early in the development process, reducing the likelihood of runtime errors. Static typing also enhances code readability and maintainability.

5. Garbage Collection:

Go features automatic garbage collection, which simplifies memory management for developers. Garbage collection helps prevent memory leaks and allows developers to focus on building features rather than managing memory.

6. Simplicity and Readability:

Go promotes a clean and simple syntax, making the code easy to read and understand. Its minimalistic approach reduces cognitive load and facilitates collaboration among team members. Go's design philosophy emphasizes simplicity and readability without sacrificing performance.

7. Cross-Platform Support:

Go is designed to be cross-platform, allowing developers to build applications that can run seamlessly on different operating systems without modification. This feature is particularly valuable in the context of backend development, where applications need to be deployed on various environments.

The Importance of Go in Backend Development

1. Scalability:

Go's concurrency model and efficient handling of resources make it well-suited for building scalable backend systems. Goroutines and channels facilitate concurrent programming, enabling developers to build highly concurrent and scalable applications effortlessly.

2. Performance:

Go is known for its performance, making it an excellent choice for building high-performance backend services. The language's efficient compilation, garbage collection, and optimized runtime contribute to low-latency and high-throughput applications.

3. Microservices Architecture:

Go is often chosen for microservices-based architectures. Its simplicity and efficiency make it easy to develop and deploy independent microservices. Go's small binary sizes and quick startup times are advantageous for building and scaling microservices.

4. Web Servers and APIs:

Go's standard library includes robust packages for building web servers and handling HTTP requests. This makes it an excellent choice for developing RESTful APIs, as demonstrated in our example of building a CRUD API. The language's simplicity, coupled with the "net/http" package, makes it easy to create efficient and scalable web services.

5. Deployment and DevOps:

Go's ability to produce static binaries simplifies deployment, as there are no external dependencies to manage. This is advantageous in a DevOps environment, where streamlined deployment processes are essential. Go's efficiency and performance also contribute to lower infrastructure costs.

 

Building a CRUD API in Go: Practical Application

Now, let's tie the advantages of Go into the practical application of building a CRUD API. In the provided example, we leveraged Go's simplicity, strong standard library, and concurrency support to create a straightforward yet powerful API. The efficiency of Go's HTTP handling, coupled with the "gorilla/mux" router, allowed us to implement robust and scalable HTTP endpoints for CRUD operations.

Setting up the Environment:

Before diving into the code, make sure you have Go installed on your machine. You can download and install Go from the official website: https://golang.org/dl/. Additionally, we'll use the "gorilla/mux" package for routing, which you can install using the following command:

go get -u github.com/gorilla/mux

 

Structuring the Project:

We'll begin by defining a simple data structure to represent a todo item. Create a file named main.go and define the Todo struct:

package main

import (
        "encoding/json"
        "fmt"
        "log"
        "net/http"
        "github.com/gorilla/mux"
)

type Todo struct {
        ID        string `json:"id,omitempty"`
        Title     string `json:"title,omitempty"`
        Completed bool   `json:"completed,omitempty"`
}

var todos = make(map[string]Todo)

Here, we've defined a Todo struct with three fields: ID, Title, and Completed. The json tags are used to specify the field names when encoding and decoding JSON.

Implementing CRUD Operations:

Next, we'll create functions for each CRUD operation: Create, Read, Update, and Delete.

1. Creating a Todo:

func CreateTodo(w http.ResponseWriter, r *http.Request) {

        var todo Todo
        _ = json.NewDecoder(r.Body).Decode(&todo)
        todo.ID = fmt.Sprintf("%d", len(todos)+1)
        todos[todo.ID] = todo
        json.NewEncoder(w).Encode(todo)

}

In this function, we decode the JSON request body into a Todo object, assign a unique ID, add it to the todos map, and then encode and send the todo as the response.

2. Reading Todos:

func GetTodos(w http.ResponseWriter, r *http.Request) {
        var todoList []Todo
        for _, value := range todos {
                todoList = append(todoList, value)
        }
        json.NewEncoder(w).Encode(todoList)
}

This function retrieves all todos from the todos map and encodes them as a JSON array in the response.

3. Reading a Single Todo:

func GetTodoByID(w http.ResponseWriter, r *http.Request) {
        params := mux.Vars(r)
        todo, exists := todos[params["id"]]

        if !exists {
                http.Error(w, "Todo not found", http.StatusNotFound)
                return
        }

        json.NewEncoder(w).Encode(todo)
}

Here, we extract the todo ID from the request parameters, check if the todo exists, and then encode and send the todo as the response.

4. Updating a Todo:

func UpdateTodo(w http.ResponseWriter, r *http.Request) {
        params := mux.Vars(r)
        todo, exists := todos[params["id"]]

        if !exists {
                http.Error(w, "Todo not found", http.StatusNotFound)
                return
        }

        var updatedTodo Todo
        _ = json.NewDecoder(r.Body).Decode(&updatedTodo)
        todo.Title = updatedTodo.Title
        todo.Completed = updatedTodo.Completed

        todos[params["id"]] = todo
        json.NewEncoder(w).Encode(todo)
}

This function updates an existing todo. It first checks if the todo exists, then decodes the JSON request body into a new Todo object and updates the fields accordingly.

5. Deleting a Todo:

func DeleteTodo(w http.ResponseWriter, r *http.Request) {
        params := mux.Vars(r)
        _, exists := todos[params["id"]]

        if !exists {
                http.Error(w, "Todo not found", http.StatusNotFound)
                return
        }
       
       delete(todos, params["id"])
        w.WriteHeader(http.StatusNoContent)
}

The delete function removes a todo from the todos map based on the provided ID.

Setting Up Routes and Server:

Now, let's set up the routes and start the server.

func main() {

        router := mux.NewRouter()

        // Define API routes

        router.HandleFunc("/todos", GetTodos).Methods("GET")

        router.HandleFunc("/todos/{id}", GetTodoByID).Methods("GET")

        router.HandleFunc("/todos", CreateTodo).Methods("POST")

        router.HandleFunc("/todos/{id}", UpdateTodo).Methods("PUT")

        router.HandleFunc("/todos/{id}", DeleteTodo).Methods("DELETE")

        // Start the server

        fmt.Println("Server is running on :8080")

        log.Fatal(http.ListenAndServe(":8080", router))
}

Here, we create a new router using mux.NewRouter(), define the API routes for each CRUD operation, and start the server on port 8080.

Testing the API Using Postman

Now that we've implemented the CRUD operations, let's test the API using tools like curl, Postman, or your browser. I have attached screenshot of the expected response go through it for a better understanding.

To create a new todo

POST http://localhost:8080/todos with a JSON payload

Go - REST API - POST

To get all todos

GET http://localhost:8080/todos

Go - REST API - GET ALL

To get a specific todo by ID

GET http://localhost:8080/todos/{id}

Go - REST API - GET

To update a todo

PUT http://localhost:8080/todos/{id} with a JSON payload

Go - REST API - UPDATE

 

To delete a todo

 DELETE http://localhost:8080/todos/{id}

Go - REST API - DELETE

Ensure you handle errors, implement proper validation, and consider using a database for a more robust solution in a production environment.

Conclusion

Building a CRUD REST API in Go is a fundamental skill for any backend developer. In this article, we've walked through the process of creating a simple API using the Go programming language and the gorilla/mux router. Understanding how to handle HTTP requests, route them to appropriate functions, and manage data effectively is crucial for building scalable and maintainable APIs. Feel free to expand on this foundation by adding features such as input validation, authentication, and database integration to meet the requirements of your specific project.

Happy coding!

Nikhil Mishra

About author
Hi there! I'm learning full-stack web development and I document my journey through blogs and technical articles. I'm passionate about building web applications from the ground up and enjoy exploring all the different technologies involved in the process.