Do you place all your application API endpoints in main.go
file. I'm pretty sure the main file has become messy with all those handler imports and endpoints at one place. Imagine getting to a situation like below and simple act of refactoring routes coming to rescue.
I have loved the idea of structuring the project based on features instead of layer based structure. Below two code snippets show comparison of feature based and layer based structure.
The benefit of feature based structure lies in the fact that we are trying to divide our project based on logical boundaries which can be independent modules/actors interacting with each other.
feature_first/
├── main.go
├── pizzas/
│ ├── handler.go
│ ├── service.go
│ ├── repository.go
│ └── model.go
├── noodles/
│ ├── handler.go
│ ├── service.go
│ ├── repository.go
│ └── model.go
└── utils/
└── logger.go
layer_first/
├── main.go
├── handlers/
│ ├── pizzas.go
│ └── noodles.go
├── services/
│ ├── pizzas.go
│ └── noodles.go
├── repositories/
│ ├── pizzas.go
│ └── noodles.go
├── models/
│ ├── pizzas.go
│ └── noodles.go
└── utils/
└── logger.go
Let's see how we can structure our routes so that we easily bake them into our feature-based project structure.
Create a sample project in a folder and initialize a go project using below command.
go mod init nested-routing
Let's install dependencies for gin using
go get -u github.com/gin-gonic/gin
Below is a sample main.go
code running a simple server and on hitting localhost:8080/ping
you will get pong as a message.
package main
import (
"net/http"
"github.com/gin-gonic/gin"
)
func main() {
r := gin.Default()
r.GET("/ping", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "pong"})
})
r.Run()
}
Modularizing Routes on Basis of features
Now let's create a router component in router/router.go
which will register routes for all our nested routes.
package router
import (
"nested-routing/noodles"
"nested-routing/pizzas"
"github.com/gin-gonic/gin"
)
func SetupRoutes(r *gin.Engine) {
api := r.Group("/api/v1")
pizzasGroup := api.Group("/pizzas")
noddlesGroup := api.Group("/noodles")
pizzas.SetupRoutes(pizzasGroup)
noodles.SetupRoutes(noddlesGroup)
}
We can now place all the routes related to noodles in the noodles package noodles/handler.go
.
package noodles
import (
"net/http"
"github.com/gin-gonic/gin"
)
func SetupRoutes(r *gin.RouterGroup) {
r.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "noodles"})
})
}
below is pizza/handler.go
.
package pizzas
import (
"net/http"
"github.com/gin-gonic/gin"
)
func SetupRoutes(r *gin.RouterGroup) {
r.GET("/", func(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"message": "pizzas"})
})
}
Ping http://localhost:8080/api/v1/noodles
to get a json response with message as noodles. Yay!, we have successfully nested our routes.
I would love to hear how you create routes in your projects. I love the little blue demon (go 🙈) and will be sharing interesting ideas and learnings regarding golang. Follow me on Dev, if you would like to join this ship to go for an adventure (pun intended).
Thank you for your time.
Top comments (0)