Registering Routes to Central Object in Go

1.7k views Asked by At

I'm working on a simple Go REST API, using Mux. Most of the tutorials I've found suggested creating the router as follows:

  • A routes.go file which contains a global array of all routes and all route handlers.
  • A router.go file which creates a router object and loops over the global array and registers everything to the router.

I'd rather use a different model:

  • A router.go file instantiates a router object and has a RegisterRoute function
  • Each file, representing a different object or component of the API, can call RegisterRoute and add additional routes to the router.

I have no idea how to do this, because it seems to require creating an initialization function for each file and calling it from main() (which defeats the purpose). Is there any accepted way to do this?

1

There are 1 answers

0
nvartolomei On BEST ANSWER

You can use func init(). It is a special function which is called right before program execution, during package initialization.

Here is an example of how to achieve what you want using gorilla/mux router, though it can be achieved with any other mux library.

package main

import (
    "fmt"
    "log"
    "net/http"

    "github.com/gorilla/mux"
)

// main.go

type Route struct {
    Name        string
    Method      string
    Pattern     string
    HandlerFunc http.HandlerFunc
}

var routes []Route

func registerRoute(route Route) {
    routes = append(routes, route)
}

func main() {
    router := mux.NewRouter().StrictSlash(false)
    for _, route := range routes {
        router.
            Methods(route.Method).
            Path(route.Pattern).
            Name(route.Name).
            Handler(route.HandlerFunc)
    }

    if err := http.ListenAndServe(":8080", router); err != nil {
        log.Fatalln(err)
    }
}

// index.go
func init() {
    registerRoute(Route{
        "index",
        "GET",
        "/",
        func(w http.ResponseWriter, r *http.Request) {
            fmt.Fprintf(w, "welcome")
        },
    })
}

// users.go
func init() {
    registerRoute(Route{
        "listUsers",
        "GET",
        "/users",
        listUsers,
    })
}

func listUsers(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "users list")
}

// products.go
func init() {
    registerRoute(Route{
        "listProducts",
        "GET",
        "/products",
        listProducts,
    })
}

func listProducts(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "products list")
}

This pattern is used by golang for example by database/sql and image packages. You can read a little bit more about this here: https://www.calhoun.io/why-we-import-packages-we-dont-actually-use-in-golang/