跳转至正文

Routing ​


Introduction ​

github.com/prismgo/framework/route is a Laravel-style routing declaration library for Gin. It wraps route definitions, groups, middleware, parameter constraints, named routes, and model binding in declarative APIs, reducing repetitive prefix concatenation and middleware management when writing raw gin.Engine routes.

Core design principles:

  • Route declarations are not bound to the HTTP server. Router only collects definitions; Mount(engine) attaches them to Gin.
  • Business handlers keep the native func(*gin.Context) signature.
  • Both a convenient global facade and isolated Router instances are supported.
  • Route metadata is collected before mounting, enabling route:list, named URL generation, and resource routes.
  • Cross-cutting checks such as parameter constraints, binding failures, and domain checks run at the front of the request chain.

Application routes are usually registered in routes/api.go, static file routes in routes/storage.go, and route listing support is implemented by the route command.

If you have just created a project, start with Getting Started to add your first HTTP endpoint. For command options, see Commands.

Quick Start ​

Using the Facade ​

go
package routes

import (
    "net/http"

    "github.com/gin-gonic/gin"
    "github.com/prismgo/framework/route"
)

func Register() {
    route.Get("/health", func(c *gin.Context) {
        c.JSON(http.StatusOK, gin.H{"status": "ok"})
    })

    route.Prefix("/api/v1").Group(func() {
        route.Post("/auth/login", login)
        route.Get("/users/{id}", showUser).WhereNumber("id").Name("users.show")
    })
}

func login(c *gin.Context)    {}
func showUser(c *gin.Context) {}

Mounting to Gin ​

go
engine := gin.New()
if err := route.Mount(engine); err != nil {
    return err
}

Using an Isolated Router ​

go
router := route.New()
router.Get("/ping", func(c *gin.Context) {
    c.String(http.StatusOK, "pong")
})

engine := gin.New()
if err := router.Mount(engine); err != nil {
    return err
}

Basic Routing ​

All route registration methods return *Route, which can be chained with names, constraints, binding failure handlers, and other options.

go
route.Get("/users", index)
route.Post("/users", store)
route.Put("/users/{id}", replace)
route.Patch("/users/{id}", update)
route.Delete("/users/{id}", destroy)
route.Options("/users", options)

Use one handler for multiple HTTP methods:

go
route.Match([]string{"PUT", "PATCH"}, "/users/{id}", update)

Register all common HTTP methods:

go
route.Any("/webhook", webhook)

Any includes GET, POST, PUT, PATCH, DELETE, OPTIONS, and HEAD.

Route Parameters ​

Laravel-style {name} parameters are recommended. Native Gin :name parameters are also supported:

go
route.Get("/users/{id}", showUser)
route.Get("/posts/{slug?}", showPost)
route.Get("/files/{path}", serveFile).Where("path", ".*")
route.Get("/prismgos/:id", showPrismgo)

Read parameters with the native Gin API:

go
func showUser(c *gin.Context) {
    id := c.Param("id")
}

Optional parameters only support the {id?} syntax. During compilation, the router generates both the parameterized and non-parameterized Gin paths.

Wildcard parameters are created by combining {path} with Where("path", ".*"). The route is compiled to Gin's *path wildcard syntax and can match multiple path segments.

Parameter Constraints ​

Parameter constraints run before the business handler. If a constraint fails, the router returns 404.

go
route.Get("/users/{id}", showUser).WhereNumber("id")
route.Get("/tags/{name}", showTag).WhereAlpha("name")
route.Get("/codes/{code}", showCode).WhereAlphaNumeric("code")
route.Get("/orders/{uuid}", showOrder).WhereUuid("uuid")
route.Get("/events/{ulid}", showEvent).WhereUlid("ulid")
route.Get("/status/{value}", showStatus).WhereIn("value", []string{"open", "closed"})
route.Get("/files/{path}", serveFile).Where("path", ".*")
MethodMeaningParametersPurpose
Where(param, expr)Custom regexparam string, expr stringSet an arbitrary regular expression constraint
WhereNumber(param)Numberparam stringRequire ^\d+$
WhereAlpha(param)Lettersparam stringRequire ^[A-Za-z]+$
WhereAlphaNumeric(param)Alphanumericparam stringRequire ^[A-Za-z0-9]+$
WhereUuid(param)UUIDparam stringRequire UUID v1-v5 format
WhereUlid(param)ULIDparam stringRequire a 26-character Crockford Base32 ULID
WhereIn(param, values)Enumparam string, values []stringRequire a value from the provided set

Group constraints apply to every route in the group that uses the same parameter name:

go
route.Prefix("/api").Where("id", `\d+`).Group(func() {
    route.Get("/users/{id}", showUser)
    route.Get("/orders/{id}", showOrder)
})

Global constraints are registered with Pattern and apply to all routes using the same parameter name. A route-level Where overrides a global Pattern:

go
route.Pattern("slug", `[a-z0-9-]+`)
route.Get("/posts/{slug}", showPost)

Named Routes ​

Named routes allow URL generation without hard-coding paths.

go
route.Get("/users/{id}", showUser).Name("users.show")

path, err := route.URL("users.show", map[string]any{"id": 100})
// path == "/users/100"

Parameters are encoded with url.PathEscape. Missing parameters return an error.

Groups may define name prefixes:

go
route.Prefix("/admin").Name("admin.").Group(func() {
    route.Get("/users/{id}", showUser).Name("users.show")
})

path, _ := route.URL("admin.users.show", map[string]any{"id": 100})
// path == "/admin/users/100"

If .Name(...) is called multiple times on the same route, the group prefix is preserved and only the route's own suffix is replaced.

Route Groups ​

Groups share path prefixes, name prefixes, middleware, domain constraints, and parameter constraints.

go
route.Prefix("/api/v1").Group(func() {
    route.Get("/users", userIndex)
    route.Get("/users/{id}", userShow)
})

Chain group attributes:

go
route.Prefix("/api/v1").
    Name("api.").
    Middleware(authMiddleware).
    Group(func() {
        route.Get("/profile", profile).Name("profile")
    })

Nested groups inherit all outer attributes:

go
route.Prefix("/api").Name("api.").Group(func() {
    route.Prefix("/admin").Name("admin.").Group(func() {
        route.Get("/users", adminUsers).Name("users.index")
    })
})

The final route name is api.admin.users.index, and the path is /api/admin/users.

Middleware ​

Group middleware applies to every route inside the group:

go
route.Prefix("/api/v1").
    Middleware(authRequired, tenantActive).
    Group(func() {
        route.Get("/profile", profile)
    })

Route-level middleware is passed before the final business action. The last handler is treated as the action:

go
route.Get("/prismgos",
    middleware.RequirePermission(permission.PrismgoList),
    app.PrismgoHandler.List,
)

Use NamedMiddleware to assign stable names, then exclude middleware by name. When a middleware is unnamed, the router falls back to the Go function name:

go
auth := route.NamedMiddleware("auth", authRequired)

route.Middleware(auth).Group(func() {
    route.Get("/profile", profile)
    route.Get("/public-info", publicInfo).WithoutMiddleware("auth")
})

You can also exclude middleware while declaring the group:

go
route.Middleware(auth).Group(func() {
    route.WithoutMiddleware("auth").Get("/callback", callback)
})

Parameter Binding ​

Parameter binders run after route parameters are parsed and before the business handler. They convert raw parameter values into business objects and write them to gin.Context. This is conceptually similar to Laravel model binding.

go
route.Bind("user", func(c *gin.Context, value string) (any, error) {
    user, err := userRepo.Find(c.Request.Context(), value)
    if err != nil {
        return nil, err
    }
    return user, nil
})

Model is a semantic alias of Bind:

go
route.Model("prismgo", bindPrismgo)

Read the bound value in a handler:

go
route.Get("/users/{user}", func(c *gin.Context) {
    value, ok := c.Get("user")
    if !ok {
        c.AbortWithStatus(http.StatusNotFound)
        return
    }
    user := value.(*model.User)
    c.JSON(http.StatusOK, user)
})

When a binder returns an error, the route's Missing handler runs if configured. Otherwise the router returns 404.

go
route.Get("/users/{user}", showUser).Missing(func(c *gin.Context) {
    c.JSON(http.StatusNotFound, gin.H{"message": "user not found"})
})

Controller Actions ​

Place a controller object on a group and register handlers by method name:

go
type UserController struct{}

func (c *UserController) Index(ctx *gin.Context) {}
func (c *UserController) Show(ctx *gin.Context)  {}

controller := &UserController{}

users := route.Controller(controller).Prefix("/users")
users.Action(http.MethodGet, "/", "Index")
users.Action(http.MethodGet, "/{id}", "Show")

Controller methods must have the signature func(*gin.Context). A nil controller, missing method, or invalid method signature panics so route configuration errors surface during startup.

Resource Routes ​

When a controller implements ResourceController, ApiResource or Resource can register REST-style routes.

go
type PhotoController struct{}

func (c *PhotoController) Index(ctx *gin.Context)   {}
func (c *PhotoController) Store(ctx *gin.Context)   {}
func (c *PhotoController) Show(ctx *gin.Context)    {}
func (c *PhotoController) Update(ctx *gin.Context)  {}
func (c *PhotoController) Destroy(ctx *gin.Context) {}

route.ApiResource("photos", &PhotoController{})

ApiResource("photos", controller) registers:

MethodURIActionName
GET/photosIndexphotos.index
POST/photosStorephotos.store
GET/photos/{photo}Showphotos.show
PUT,PATCH/photos/{photo}Updatephotos.update
DELETE/photos/{photo}Destroyphotos.destroy

Resource additionally supports Create and Edit. Those routes are registered only when the controller implements CreateController or EditController.

go
route.Resource("photos", &FullPhotoController{})

Options:

go
route.ApiResource("photos", controller, route.Only("index", "show"))
route.ApiResource("photos", controller, route.Except("destroy"))

route.Resource("widgets", controller,
    route.Names(map[string]string{
        "index": "widgets",
        "show":  "widgets.detail",
    }),
)

route.Resource("widgets", controller,
    route.Parameters(map[string]string{
        "widgets": "widget_id",
    }),
)

Register multiple API resources:

go
route.ApiResources(map[string]route.ResourceController{
    "photos": photoController,
    "posts":  postController,
})

Dots in resource names become path separators:

go
route.ApiResource("users.photos", controller)
// /users/photos
// /users/photos/{photo}

Redirect Routes ​

go
route.Redirect("/old", "/new")
route.Redirect("/temp", "/target", http.StatusTemporaryRedirect)
route.PermanentRedirect("/old", "/new")

Static Files ​

go
route.Static("/assets", "./public/assets")

This registers GET /assets/*filepath and serves files through http.Dir(root).

Fallback Routes ​

Fallback handlers run when no route matches. This project uses a fallback route for SPA fallback: non-/api paths return public/index.html.

go
route.Fallback(func(c *gin.Context) {
    c.JSON(http.StatusNotFound, gin.H{"error": "not found"})
})

Domain Constraints ​

Domain constraints check the Host header before the business handler. Non-matching hosts return 404.

go
route.Domain("api.example.test").Group(func() {
    route.Get("/health", health)
})

Placeholders are supported for subdomain segments:

go
route.Domain("{tenant}.example.test").Group(func() {
    route.Get("/dashboard", dashboard)
})

Rate Limiting ​

Register a named limiter:

go
route.RateLimiter("login", func(c *gin.Context) []route.Limit {
    return []route.Limit{
        route.PerMinute(5).By(func(c *gin.Context) string {
            return c.ClientIP()
        }),
    }
})

Attach it to routes:

go
route.Post("/auth/login", route.Throttle("login"), login)

route.Middleware(route.Throttle("api")).Group(func() {
    route.Get("/profile", profile)
})

Limiter state uses github.com/prismgo/framework/cache. If the named limiter does not exist, Throttle allows the request through. Over-limit requests return 429 with {"message":"too many requests"}.

Current Route Information ​

Before the business handler runs, every mounted route writes its metadata to gin.Context under the key route.current:

go
func handler(c *gin.Context) {
    value, ok := c.Get("route.current")
    if !ok {
        return
    }

    info := value.(route.RouteInfo)
}
FieldTypeDescription
Methods[]stringHTTP methods
URIstringOriginal declared URI
GinPathstringCompiled Gin path
NamestringRoute name
DomainstringDomain constraint
HandlerstringBusiness handler function name
Middleware[]stringMiddleware names
SourcePathstringSource file path of the business handler

Route List and Debugging ​

In code:

go
routes := route.List()
for _, item := range routes {
    fmt.Println(item.Methods, item.URI, item.Name)
}

From the command line:

powershell
go run ./ route:list
go run ./ route:list --path=/api/v1
go run ./ route:list --name=users
go run ./ route:list --method=POST

Handler Execution Order ​

Mount(engine) attaches routes to Gin in declaration order. Each route's handler chain runs as follows:

  1. Domain constraint check.
  2. Parameter regex constraint check.
  3. Parameter binders.
  4. Group and route middleware.
  5. Current route information injection.
  6. Business action.

Business middleware that depends on bound objects or permission context should be placed in the route middleware stage. Parameter format validation and binding failure handling run before business middleware.

Global Facade Lifecycle ​

go
router := route.Resolve()

Resolve reads the singleton under the "route.router" key from the Application Container. The first call creates the Router through the ServiceProvider lazy factory.

Tests should prefer isolated routers to avoid polluting the global facade:

go
router := route.New()
router.Get("/health", handler)
router.Reset()

Router API Reference ​

Constructors ​

MethodSignaturePurpose
Newfunc New() *RouterCreate an empty router
Resetfunc (r *Router) Reset()Clear routes, named index, binders, and global constraints
Clonefunc (r *Router) Clone() *RouterDeep-copy the current route table without declaration-time group scope

Route Registration ​

MethodSignaturePurpose
Addfunc (r *Router) Add(methods []string, uri string, handlers ...HandlerFunc) *RouteLow-level registration; the last handler is the action
Getfunc (r *Router) Get(uri string, handlers ...HandlerFunc) *RouteRegister GET
Postfunc (r *Router) Post(uri string, handlers ...HandlerFunc) *RouteRegister POST
Putfunc (r *Router) Put(uri string, handlers ...HandlerFunc) *RouteRegister PUT
Patchfunc (r *Router) Patch(uri string, handlers ...HandlerFunc) *RouteRegister PATCH
Deletefunc (r *Router) Delete(uri string, handlers ...HandlerFunc) *RouteRegister DELETE
Optionsfunc (r *Router) Options(uri string, handlers ...HandlerFunc) *RouteRegister OPTIONS
Matchfunc (r *Router) Match(methods []string, uri string, handlers ...HandlerFunc) *RouteRegister a route for selected methods
Anyfunc (r *Router) Any(uri string, handlers ...HandlerFunc) *RouteRegister all common methods

Special Routes ​

MethodSignaturePurpose
Redirectfunc (r *Router) Redirect(uri, destination string, status ...int) *RouteRegister a redirect route, default 302
PermanentRedirectfunc (r *Router) PermanentRedirect(uri, destination string) *RouteRegister a permanent 301 redirect
Staticfunc (r *Router) Static(uri, root string) *RouteRegister a static file directory
Fallbackfunc (r *Router) Fallback(handler HandlerFunc) *RouteRegister a NoRoute fallback

Declarative Entry Points ​

MethodSignaturePurpose
Prefixfunc (r *Router) Prefix(prefix string) *RegistrarCreate a registrar with a path prefix
Namefunc (r *Router) Name(name string) *RegistrarCreate a registrar with a name prefix
Domainfunc (r *Router) Domain(domain string) *RegistrarCreate a registrar with a domain constraint
Middlewarefunc (r *Router) Middleware(handlers ...HandlerFunc) *RegistrarCreate a registrar with middleware
Controllerfunc (r *Router) Controller(controller any) *RegistrarCreate a registrar with a controller
Groupfunc (r *Router) Group(fn func())Execute a group closure directly on the router

Global Configuration ​

MethodSignaturePurpose
Bindfunc (r *Router) Bind(param string, binder Binder)Register a parameter binder
Modelfunc (r *Router) Model(param string, binder Binder)Semantic alias of Bind
Patternfunc (r *Router) Pattern(param, expr string)Register a global parameter constraint

Query and Mount ​

MethodSignaturePurpose
Listfunc (r *Router) List() []RouteInfoReturn a snapshot of all routes
URLfunc (r *Router) URL(name string, params map[string]any) (string, error)Generate a URL from a named route
Mountfunc (r *Router) Mount(engine *gin.Engine) errorAttach the route table to a Gin engine

Route Chain API Reference ​

Route is the chainable configuration object returned by route registration methods.

MethodSignaturePurpose
Namefunc (r *Route) Name(name string) *RouteSet the route name suffix, replacing previous calls
Wherefunc (r *Route) Where(param, expr string) *RouteSet a parameter regex constraint
WhereNumberfunc (r *Route) WhereNumber(param string) *RouteRequire a numeric parameter
WhereAlphafunc (r *Route) WhereAlpha(param string) *RouteRequire letters only
WhereAlphaNumericfunc (r *Route) WhereAlphaNumeric(param string) *RouteRequire letters or digits
WhereUuidfunc (r *Route) WhereUuid(param string) *RouteRequire a UUID
WhereUlidfunc (r *Route) WhereUlid(param string) *RouteRequire a ULID
WhereInfunc (r *Route) WhereIn(param string, values []string) *RouteRequire one of the listed values
Missingfunc (r *Route) Missing(handler HandlerFunc) *RouteSet the fallback handler for binding failure
Middlewarefunc (r *Route) Middleware(handlers ...HandlerFunc) *RouteAppend route-level middleware
WithoutMiddlewarefunc (r *Route) WithoutMiddleware(names ...string) *RouteExclude middleware by name or function name
ScopeBindingsfunc (r *Route) ScopeBindings() *RoutePreserve Laravel chain semantics; currently no side effect
WithoutScopedBindingsfunc (r *Route) WithoutScopedBindings() *RoutePreserve Laravel chain semantics; currently no side effect

Registrar API Reference ​

Registrar stores attributes used in chainable group declarations.

Attribute Methods ​

MethodSignaturePurpose
Prefixfunc (r *Registrar) Prefix(prefix string) *RegistrarAppend a path prefix
Namefunc (r *Registrar) Name(name string) *RegistrarAppend a name prefix
Domainfunc (r *Registrar) Domain(domain string) *RegistrarSet a domain constraint, replacing the outer value
Middlewarefunc (r *Registrar) Middleware(handlers ...HandlerFunc) *RegistrarAppend group middleware
WithoutMiddlewarefunc (r *Registrar) WithoutMiddleware(names ...string) *RegistrarExclude selected middleware from the group
Controllerfunc (r *Registrar) Controller(controller any) *RegistrarSet the controller object, replacing the outer value
Wherefunc (r *Registrar) Where(param, expr string) *RegistrarAppend a group parameter constraint
ScopeBindingsfunc (r *Registrar) ScopeBindings() *RegistrarPreserve Laravel chain semantics
WithoutScopedBindingsfunc (r *Registrar) WithoutScopedBindings() *RegistrarPreserve Laravel chain semantics

Registration and Group Methods ​

MethodSignaturePurpose
Groupfunc (r *Registrar) Group(fn func())Execute a group closure whose routes inherit current attributes
Getfunc (r *Registrar) Get(uri string, handlers ...HandlerFunc) *RouteRegister GET
Postfunc (r *Registrar) Post(uri string, handlers ...HandlerFunc) *RouteRegister POST
Putfunc (r *Registrar) Put(uri string, handlers ...HandlerFunc) *RouteRegister PUT
Patchfunc (r *Registrar) Patch(uri string, handlers ...HandlerFunc) *RouteRegister PATCH
Deletefunc (r *Registrar) Delete(uri string, handlers ...HandlerFunc) *RouteRegister DELETE
Optionsfunc (r *Registrar) Options(uri string, handlers ...HandlerFunc) *RouteRegister OPTIONS
Matchfunc (r *Registrar) Match(methods []string, uri string, handlers ...HandlerFunc) *RouteRegister selected methods
Anyfunc (r *Registrar) Any(uri string, handlers ...HandlerFunc) *RouteRegister all methods
Redirectfunc (r *Registrar) Redirect(uri, destination string, status ...int) *RouteRegister a redirect route
PermanentRedirectfunc (r *Registrar) PermanentRedirect(uri, destination string) *RouteRegister a permanent 301 redirect
Actionfunc (r *Registrar) Action(method, uri, action string, middleware ...HandlerFunc) *RouteRegister a controller method by name

Resource Route API Reference ​

InterfaceMethodsPurpose
ResourceControllerIndex, Store, Show, Update, DestroyRequired for API resource routes
CreateControllerCreateOptional for full resource routes
EditControllerEditOptional for full resource routes
FunctionSignaturePurpose
Resourcefunc Resource(name string, controller ResourceController, options ...ResourceOption) []*RouteRegister a full resource route set
ApiResourcefunc ApiResource(name string, controller ResourceController, options ...ResourceOption) []*RouteRegister API resource routes
ApiResourcesfunc ApiResources(resources map[string]ResourceController, options ...ResourceOption) []*RouteRegister multiple API resources
OptionSignaturePurpose
Onlyfunc Only(actions ...string) ResourceOptionRegister only selected actions
Exceptfunc Except(actions ...string) ResourceOptionExclude selected actions
Namesfunc Names(names map[string]string) ResourceOptionCustomize route names
Parametersfunc Parameters(parameters map[string]string) ResourceOptionCustomize resource parameter names

Rate Limiting API Reference ​

Type / functionSignaturePurpose
Limitstruct {Max, Every, KeyFunc}Describe one limiter window
PerMinutefunc PerMinute(max int) LimitBuild a per-minute limiter
Byfunc (l Limit) By(fn func(*gin.Context) string) LimitSet the limiter key source
RateLimiterfunc RateLimiter(name string, limiter RateLimiterFunc)Register a named limiter
Throttlefunc Throttle(name string) gin.HandlerFuncReturn limiter middleware for routes or groups

Global Facade Function Reference ​

FunctionSignaturePurpose
Resolvefunc Resolve() *RouterGet the global router
Bindfunc Bind(param string, binder Binder)Register a parameter binder
Modelfunc Model(param string, binder Binder)Semantic alias of Bind
Patternfunc Pattern(param, expr string)Register a global parameter constraint
Mountfunc Mount(engine *gin.Engine) errorMount routes to Gin
Listfunc List() []RouteInfoReturn route snapshots
URLfunc URL(name string, params map[string]any) (string, error)Generate a named route URL
Getfunc Get(uri string, handlers ...HandlerFunc) *RouteRegister GET
Postfunc Post(uri string, handlers ...HandlerFunc) *RouteRegister POST
Putfunc Put(uri string, handlers ...HandlerFunc) *RouteRegister PUT
Patchfunc Patch(uri string, handlers ...HandlerFunc) *RouteRegister PATCH
Deletefunc Delete(uri string, handlers ...HandlerFunc) *RouteRegister DELETE
Optionsfunc Options(uri string, handlers ...HandlerFunc) *RouteRegister OPTIONS
Matchfunc Match(methods []string, uri string, handlers ...HandlerFunc) *RouteRegister multiple methods
Anyfunc Any(uri string, handlers ...HandlerFunc) *RouteRegister all methods
Redirectfunc Redirect(uri, destination string, status ...int) *RouteRegister a redirect
PermanentRedirectfunc PermanentRedirect(uri, destination string) *RouteRegister a permanent redirect
Staticfunc Static(uri, root string) *RouteRegister static files
Fallbackfunc Fallback(handler HandlerFunc) *RouteRegister a NoRoute fallback
Prefixfunc Prefix(prefix string) *RegistrarCreate a prefix registrar
Namefunc Name(name string) *RegistrarCreate a name-prefix registrar
Domainfunc Domain(domain string) *RegistrarCreate a domain registrar
Middlewarefunc Middleware(handlers ...HandlerFunc) *RegistrarCreate a middleware registrar
WithoutMiddlewarefunc WithoutMiddleware(names ...string) *RegistrarCreate a registrar excluding middleware
Controllerfunc Controller(controller any) *RegistrarCreate a controller registrar
Groupfunc Group(fn func())Execute a group closure
Resourcefunc Resource(name string, controller ResourceController, options ...ResourceOption) []*RouteRegister full resource routes
ApiResourcefunc ApiResource(name string, controller ResourceController, options ...ResourceOption) []*RouteRegister API resource routes
ApiResourcesfunc ApiResources(resources map[string]ResourceController, options ...ResourceOption) []*RouteRegister multiple API resources

ServiceProvider ​

ServiceProvider registers Router in the Application Container and creates it lazily on first Resolve:

go
type ServiceProvider struct{}

func (ServiceProvider) Name() string { return "route" }
func (ServiceProvider) Register(app providerApplication) error { /* ... */ }
func (ServiceProvider) Boot(providerApplication) error { return nil }
  • Container binding key: "route.router".
  • If the caller has explicitly injected a Router, the provider keeps it.
  • The binding uses singleton semantics, so there is one global Router instance.

Business routes should live in routes/api.go; infrastructure routes should live in routes/storage.go.

Recommended pattern:

go
route.Prefix("/api/v1").
    Middleware(middleware.AuthRequired(), tenantActive, injectPerms).
    Group(func() {
        route.Get("/prismgos",
            middleware.RequirePermission(permission.PrismgoList),
            app.PrismgoHandler.List,
        )
    })

Recommendations for new routes:

  • Use Prefix groups when routes share a common prefix.
  • Put shared authentication, tenant, audit, and similar logic on groups.
  • Add fine-grained permission middleware explicitly before the action.
  • Prefer {id} syntax for new parameters to stay consistent with Laravel-style routes.
  • Add Name when a route needs URL generation or external references.
  • Use constraints such as WhereNumber and WhereIn so invalid requests return 404 before entering the business layer.