/octo-go

Octo Go is a lightweight, simple and customizable router for building a powerful HTTP endpoints in Go

Primary LanguageGoGNU General Public License v3.0GPL-3.0


Logo

Octo Go

Simple router for Go

Report Bug · Request Feature · Changelog

Table of Contents
  1. About The Project
  2. Getting Started
  3. Usage
  4. License
  5. Acknowledgments

About the project

Octo Go is a lightweight, high-performance router for creating robust HTTP endpoints in Go. It started its history as a router built entirely from scratch, but with the release of Go 1.22 Octo Go turned into a wrapper around the original mux, adding convenient creation of routes, middleware support and group routes. Octo Go is the perfect tool for simple creating powerful APIs in Go, staying as close to the original mux as possible, without unnecessary complexity.

back to top

Getting started

func main() {
  mux := router.NewRouter()

  mux.GET("/user", userGetHandler)
  mux.POST("/user", userPostHandler)

  http.ListenAndServe("localhost:3000", mux)

  // Registering route
  // METHOD: GET, POST, PUT, PATCH, DELETE
  // PATH: Route path e.g "/", "/users" or with parameters "/users/{id}", "/users/{id}/{group}"
  // HANDLER: func(w http.ResponseWriter, r *http.Request)
  // MIDDLEWARES: func(http.ResponseWriter, *http.Request, func()) you can add as many as you want

  mux.METHOD(PATH, HANDLER, MIDDLEWARES)

  // Registering global middleware
  // MIDDLEWARE: func(http.ResponseWriter, *http.Request, func())

  mux.Use(MIDDLEWARE)

  // Registering group of routes
  // BASE_PATH: default path for this group e.g "/products", all methods registered inside starts with this path

  mux.Group(BASE_PATH, func(mux *router.Router) {
    mux.METHOD(PATH, HANDLER, MIDDLEWARES)
  }, MIDDLEWARES)
}

Prerequisites

Go 1.22

Installation

go get -u github.com/4c65736975/octo-go

back to top

Usage

Routes

mux.GET("/", getHandler)
mux.PUT("/", putHandler)
mux.POST("/", postHandler)
mux.PATCH("/", patchHandler)
mux.DELETE("/", deleteHandler)

Group Routes

mux.Group("/users", func(mux *router.Router) {
  mux.GET("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "List all users")
  })
  mux.GET("/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    fmt.Fprintf(w, "Get user with ID %s\n", id)
  })
  mux.POST("/", func(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintln(w, "Create a new user")
  })
  mux.PUT("/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    fmt.Fprintf(w, "Update user with ID %s\n", id)
  })
  mux.DELETE("/{id}", func(w http.ResponseWriter, r *http.Request) {
    id := r.PathValue("id")
    fmt.Fprintf(w, "Delete user with ID %s\n", id)
  })
})

mux.Group("/products", func(mux *router.Router) {
  mux.GET("/", getProductsHandler)
  mux.GET("/{id}", getProductHandler)
  mux.POST("/", createProductHandler)
  mux.PUT("/{id}", updateProductHandler)
  mux.DELETE("/{id}", deleteProductHandler)
})

Middlewares

func exampleMiddleware(w http.ResponseWriter, req *http.Request, next func()) {
  fmt.Fprintln(w, "Example middleware")
  next()
}

If the next() function is not executed in the middleware, the request ends with this middleware, if it is executed, the next middleware or final handler is executed.

Global Middlewares

mux.Use(loggingMiddleware)
mux.Use(ipMiddleware)

Global middleware works on every registered route after adding global middleware, all routes added earlier are ignored

mux.GET("/products", productsHandler) // not using loggingMiddleware
mux.Use(loggingMiddleware)
mux.GET("/products/{id}", productHandler) // using loggingMiddleware
mux.DELETE("/products/{id}", deleteProductHandler) // using loggingMiddleware

Route Middlewares

mux.GET("/profile", profileHandler, authMiddleware)
mux.GET("/profile/settings", settingsHandler, authMiddleware, settingsMiddleware, usageMiddleware)

Group Middlewares

mux.Group("/users", func(mux *router.Router) {
  mux.GET("/", usersHandler)
  mux.DELETE("/{id}", deleteUserHandler, authMiddleware)
}, telemetryMiddleware)

Middlewares are executed in the order in which they are registered, unless the next() function is called before the middleware code as in the case below

mux.GET("/", routeHandler, localMiddleware, localMiddleware2, localMiddleware3, localMiddleware4)

func localMiddleware(w http.ResponseWriter, req *http.Request, next func()) {
  fmt.Println("Local middleware 1")
  next()
}

func localMiddleware2(w http.ResponseWriter, req *http.Request, next func()) {
  next()
  fmt.Println("Local middleware 2")
}

func localMiddleware3(w http.ResponseWriter, req *http.Request, next func()) {
  fmt.Println("Local middleware 3")
  next()
}

func localMiddleware4(w http.ResponseWriter, req *http.Request, next func()) {
  fmt.Println("Local middleware 4")
  next()
}

Console Output:
Local middleware 1
Local middleware 3
Local middleware 4
Local middleware 2

Parameters

With Go 1.22 we can register dynamic routes using the built-in mux. We can get the parameter in the handler or middleware as shown below.

mux.GET("/users/{id}/products/{category}", func(w http.ResponseWriter, r *http.Request) {
  fmt.Println("Handler")
  fmt.Println(req.PathValue("id"))
  fmt.Println(req.PathValue("category"))
}, testMiddleware)

func testMiddleware(w http.ResponseWriter, req *http.Request, next func()) {
  fmt.Println("Middleware")
  fmt.Println(req.PathValue("id"))
  fmt.Println(req.PathValue("category"))
  next()
}

Requested route: /users/1/products/human

Console Output:
Middleware
1
human
Handler
1
human

Query

We can access the query value as with the default mux and similarly to parameters, in the handler and middleware.

mux.GET("/users/{id}/products/{category}", func(w http.ResponseWriter, r *http.Request) {
  fmt.Println("Handler")
  fmt.Println(req.URL.Query())
  fmt.Println(req.URL.Query().Get("maxHeight"))
}, testMiddleware)

func testMiddleware(w http.ResponseWriter, req *http.Request, next func()) {
  fmt.Println("Middleware")
  fmt.Println(req.URL.Query())
  fmt.Println(req.URL.Query().Get("maxHeight"))
  next()
}

Requested route: /users/1/products/human?sort=asc&maxHeight=180

Console Output:
Middleware
map[maxHeight:[180] sort:[asc]]
180
Handler
map[maxHeight:[180] sort:[asc]]
180

back to top

License

Distributed under the GPL-3.0 license. See LICENSE for more information.

back to top

Acknowledgments

back to top