Go

 

Building RESTful APIs with Go and the Chi Framework

In the world of web development, building robust and efficient RESTful APIs is a common requirement. Go, also known as Golang, has gained popularity for its performance and simplicity, making it an excellent choice for creating RESTful APIs. To streamline the development process and make it even more efficient, we have the Chi framework at our disposal. In this blog, we’ll explore how to build RESTful APIs with Go and the Chi framework, step by step.

Go for Financial Applications: Handling Currency and Transactions

1. Prerequisites

Before we dive into building RESTful APIs with Go and the Chi framework, make sure you have the following prerequisites in place:

  • Go Installed: Ensure you have Go installed on your system. You can download and install it from the official Go website.
  • Text Editor or IDE: Choose a text editor or integrated development environment (IDE) for writing and running your Go code. Popular choices include Visual Studio Code, GoLand, and Sublime Text.
  • Basic Knowledge of Go: Familiarize yourself with the basics of the Go programming language. If you’re new to Go, the official Go Tour is a great place to start.

2. Setting Up Your Project

Let’s start by setting up a new Go project for building our RESTful API with the Chi framework. Follow these steps:

2.1. Create a Project Directory

Create a new directory for your project. You can do this using the mkdir command in your terminal.

bash
mkdir my-api-project
cd my-api-project

2.2. Initialize a Go Module

Initialize a Go module in your project directory. This will help manage your project’s dependencies.

bash
go mod init my-api-project

2.3. Install Chi

Install the Chi framework by running the following command:

bash
go get github.com/go-chi/chi

3. Building a Simple RESTful API

Now that our project is set up, let’s create a simple RESTful API using Chi. We’ll build an API that allows us to manage a list of tasks. Our API will support basic CRUD (Create, Read, Update, Delete) operations.

3.1. Define Your Data Model

In this example, we’ll work with a simple task model that has a title and a description. Create a task.go file in your project directory to define the data model:

go
package main

type Task struct {
    ID          int    `json:"id"`
    Title       string `json:"title"`
    Description string `json:"description"`
}

3.2. Create the API Routes

Now, let’s create the API routes for our tasks. We’ll define routes for creating, reading, updating, and deleting tasks. Create a main.go file and add the following code:

go
package main

import (
    "encoding/json"
    "net/http"
    "strconv"

    "github.com/go-chi/chi"
)

var tasks = make(map[int]Task)
var lastID = 0

func main() {
    r := chi.NewRouter()

    r.Post("/tasks", createTask)
    r.Get("/tasks/{id}", getTask)
    r.Get("/tasks", listTasks)
    r.Put("/tasks/{id}", updateTask)
    r.Delete("/tasks/{id}", deleteTask)

    http.ListenAndServe(":8080", r)
}

func createTask(w http.ResponseWriter, r *http.Request) {
    var task Task
    err := json.NewDecoder(r.Body).Decode(&task)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    lastID++
    task.ID = lastID
    tasks[lastID] = task

    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(task)
}

func getTask(w http.ResponseWriter, r *http.Request) {
    idStr := chi.URLParam(r, "id")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, "Invalid task ID", http.StatusBadRequest)
        return
    }

    task, found := tasks[id]
    if !found {
        http.Error(w, "Task not found", http.StatusNotFound)
        return
    }

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

func listTasks(w http.ResponseWriter, r *http.Request) {
    taskList := make([]Task, 0, len(tasks))
    for _, task := range tasks {
        taskList = append(taskList, task)
    }

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

func updateTask(w http.ResponseWriter, r *http.Request) {
    idStr := chi.URLParam(r, "id")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, "Invalid task ID", http.StatusBadRequest)
        return
    }

    var updatedTask Task
    err = json.NewDecoder(r.Body).Decode(&updatedTask)
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }

    _, found := tasks[id]
    if !found {
        http.Error(w, "Task not found", http.StatusNotFound)
        return
    }

    updatedTask.ID = id
    tasks[id] = updatedTask

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

func deleteTask(w http.ResponseWriter, r *http.Request) {
    idStr := chi.URLParam(r, "id")
    id, err := strconv.Atoi(idStr)
    if err != nil {
        http.Error(w, "Invalid task ID", http.StatusBadRequest)
        return
    }

    _, found := tasks[id]
    if !found {
        http.Error(w, "Task not found", http.StatusNotFound)
        return
    }

    delete(tasks, id)
    w.WriteHeader(http.StatusNoContent)
}

This code defines a simple RESTful API with routes for creating, reading, updating, and deleting tasks. It uses the Chi router to handle HTTP requests.

3.3. Run the API

To run the API, use the following command:

bash
go run main.go

Your API should now be running on http://localhost:8080.

4. Testing Your RESTful API

Now that you have built your RESTful API, it’s essential to test it to ensure it works as expected. You can use tools like curl, Postman, or write test cases in Go using the testing package.

4.1. Using curl

Here are some curl commands to test your API:

Create a Task:

bash
curl -X POST -H "Content-Type: application/json" -d '{"title": "Task 1", "description": "Description 1"}' http://localhost:8080/tasks

Get a Task:

bash
curl http://localhost:8080/tasks/1

List Tasks:

bash
curl http://localhost:8080/tasks

Update a Task:

bash
curl -X PUT -H "Content-Type: application/json" -d '{"title": "Updated Task 1", "description": "Updated Description 1"}' http://localhost:8080/tasks/1

Delete a Task:

bash
curl -X DELETE http://localhost:8080/tasks/1

5. Writing Go Test Cases

You can also write test cases in Go to ensure the correctness of your API. Create a main_test.go file in your project directory with the following code:

go
package main

import (
    "encoding/json"
    "net/http"
    "net/http/httptest"
    "strings"
    "testing"

    "github.com/go-chi/chi"
)

func TestAPI(t *testing.T) {
    r := chi.NewRouter()
    r.Post("/tasks", createTask)
    r.Get("/tasks/{id}", getTask)
    r.Get("/tasks", listTasks)
    r.Put("/tasks/{id}", updateTask)
    r.Delete("/tasks/{id}", deleteTask)

    srv := httptest.NewServer(r)
    defer srv.Close()

    t.Run("CreateTask", func(t *testing.T) {
        payload := `{"title": "Task 1", "description": "Description 1"}`
        resp, err := http.Post(srv.URL+"/tasks", "application/json", strings.NewReader(payload))
        if err != nil {
            t.Fatalf("Expected no error, got %v", err)
        }
        defer resp.Body.Close()

        if resp.StatusCode != http.StatusCreated {
            t.Errorf("Expected status code %d, got %d", http.StatusCreated, resp.StatusCode)
        }

        var task Task
        if err := json.NewDecoder(resp.Body).Decode(&task); err != nil {
            t.Fatalf("Expected no error, got %v", err)
        }

        if task.ID == 0 {
            t.Error("Expected non-zero task ID")
        }
    })

    // Add more test cases for GET, PUT, and DELETE operations
}

In this example, we use the httptest package to create an HTTP server for testing our API. You can expand this test suite to cover all the endpoints and scenarios of your API.

Conclusion

Building RESTful APIs with Go and the Chi framework is a powerful and efficient way to create robust web services. In this blog, we covered the basics of setting up a Go project, defining API routes, and testing your API. You can now use this knowledge to develop RESTful APIs for various applications and services. Remember to follow best practices for error handling, input validation, and security when building production-ready APIs. Happy coding!

Previously at
Flag Argentina
Mexico
time icon
GMT-6
Over 5 years of experience in Golang. Led the design and implementation of a distributed system and platform for building conversational chatbots.