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
Lets write a basic run command in our
Makefile
run:
@go run main.go
main.go
package main
import "fmt"
func main() {
fmt.Println("Hello, go-chi!!")
}
Now, run the command to install go-chi
go get -u github.com/go-chi/chi/v5
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)
}
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
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)
}
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!!
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)
}
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}]
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
andAuthorization
, 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)