DEV Community

arcade
arcade

Posted on • Updated on

Write APIs in Go using go-chi

In this blog post you'll learn how to write REST APIs in Go using go-chi.

Intro

go-chi is a lightweight, idiomatic and composable router for building Go HTTP services (copied directly from the webiste).

PreRequisites

Go 1.1x or higher should be install in your machine.

Code

So, we will start of with writing all of our code in one single file main.go, as there is no need to follow a folder structure pattern for such simple example.

└── go-api
    ├── go.mod
    ├── go.sum
    ├── main.go
    └── Makefile
Enter fullscreen mode Exit fullscreen mode

Lets write a basic run command in our

Makefile

run:
    @go run main.go
Enter fullscreen mode Exit fullscreen mode

main.go

package main

import "fmt"

func main() {
    fmt.Println("Hello, go-chi!!")
}

Enter fullscreen mode Exit fullscreen mode

Now, run the command to install go-chi

go get -u github.com/go-chi/chi/v5
Enter fullscreen mode Exit fullscreen mode

Lets start of with creating a server struct that will hold the Router provided by go-chi.

package main

import (
    "fmt"
    "net/http"

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

type Server struct {
    Router *chi.Mux
}

func CreateServer() *Server {
    server := &Server{
        Router: chi.NewRouter(),
    }
    return server
}

func main() {
    server := CreateServer()
    fmt.Println("server running on port:5000")
    http.ListenAndServe(":5000", server.Router)
}
Enter fullscreen mode Exit fullscreen mode

Run the command make run in your terminal and you see the server running

  go-api git:(main)  make run
server running on port:5000

Enter fullscreen mode Exit fullscreen mode

But we can't hit any endpoints as we haven't create any. So, lets create a basic GET /greet endpoint

func Greet(w http.ResponseWriter, r *http.Request) {
    w.Write([]byte("Hello, World!!"))
}

func (server *Server) MountHandlers() {
    server.Router.Get("/greet", Greet)
}

func main() {
    server := CreateServer()
    server.MountHandlers()

    fmt.Println("server running on port:5000")
    http.ListenAndServe(":5000", server.Router)
}
Enter fullscreen mode Exit fullscreen mode

As you can see, we created a basic Greet handler which will return "Hello, World!!" when you hit http://localhost:5000/greet

  go-api git:(main)  curl -X GET 'http://localhost:5000/greet'
Hello, World!!
Enter fullscreen mode Exit fullscreen mode

Now, lets create CRUD endpoints for a basic Todos app.

func (server *Server) MountHandlers() {
    server.Router.Get("/greet", Greet)

    todosRouter := chi.NewRouter()
    todosRouter.Group(func(r chi.Router) {
        r.Get("/", GetTodos)
        r.Post("/", AddTodo)
    })

    server.Router.Mount("/todos", todosRouter)
}

type Todo struct {
    Task      string `json:"task"`
    Completed bool   `json:"completed"`
}

var Todos []*Todo

func AddTodo(w http.ResponseWriter, r *http.Request) {
    todo := new(Todo)
    if err := json.NewDecoder(r.Body).Decode(todo); err != nil {
        w.WriteHeader(http.StatusBadRequest)
        w.Write([]byte("Please enter a correct Todo!!"))
        return
    }
    Todos = append(Todos, todo)
    w.WriteHeader(http.StatusOK)
    w.Write([]byte("Todo added!!"))
}

func GetTodos(w http.ResponseWriter, r *http.Request) {
    w.WriteHeader(http.StatusOK)
    json.NewEncoder(w).Encode(Todos)
}

Enter fullscreen mode Exit fullscreen mode

We can create different Group of endpoints and then we can mount them to our main Router with their own context path.

In the above code, we created a two endpoints GET /todos (returns todos), POST /todos (adds todos to Todos array). We store these Todos in an in-memory array, the code written in AddTodo function, we first decode request body in Todo struct, if there an error, we return Bad request, else we add the destructed todo into our array.

Hit the below endpoints using curl

  go-api git:(main)  curl -X POST 'http://localhost:5000/todos' -d '{"task": "Learn Go", "completed": false}'
Todo added!!

  go-api git:(main)  curl -X GET 'http://localhost:5000/todos'
[{"task":"Learn Go","completed":false}]
Enter fullscreen mode Exit fullscreen mode

I encourage you to write PUT /todos, DELETE /todos endpoint on your own.

Conclusion

That's it, this was starter program more like an introduction, but in the upcomming blogs, we'll cover multiple topics like

  • Connecting to databases like MySQL, MongoDB etc
  • User Authentication and Authorization, Protected Routes etc
  • In-memory Caching, using Redis cache etc
  • Logging and many more!!

Github: https://github.com/the-arcade-01/go-api

Thanks for reading till the end, really appreciate it!!

Top comments (0)