Skip to content

Routes

Routes connect HTTP methods and paths to handlers through the web routing contract.

GoForj Apps keep route registration explicit so HTTP behavior is discoverable through code, route:list, and enabled metrics, Inspects, and Lighthouse.

Where Routes Live

Application route composition usually lives in:

text
app/routes.go

Feature packages expose routes from controllers:

text
internal/users/controller.go
internal/reports/controller.go

The app route file composes those controller routes into route groups. Additional apps use the same pattern under the owning app's directory:

text
app/admin/routes.go

Follow the Complete Users Route

Use JSON API Route as the canonical, runnable Users workflow. It is maintained against the current templates; this page only describes the route-composition contract.

bash
forj make:controller users

The generator creates the controller and adds its constructor to app/wire/inject_http_controllers_app.go and its routes to app/routes.go. After replacing the generated handler with application behavior, add any service constructors it needs to app/wire/inject_services_app.go, then run the scenario's build, test, and endpoint-verification steps. forj route:list is the check that the registered route is present.

Controller Routes

Controllers return []web.Route:

go
func (c *Controller) Routes() []web.Route {
	return []web.Route{
		web.NewRoute(http.MethodGet, "/users/:id", c.Show),
		web.NewRoute(http.MethodPost, "/users", c.Store),
	}
}

Handlers receive web.Context, not the underlying HTTP engine context.

Route Groups

The generated router groups public and protected routes under /api/v1 by default:

go
func ProvideRoutes(r *AppRoutes, authService *auth.Service) []web.RouteGroup {
	var groups []web.RouteGroup

	if len(r.public) > 0 {
		groups = append(groups, web.NewRouteGroup("/api/v1", r.public))
	}
	if len(r.protected) > 0 {
		groups = append(groups,
			web.NewRouteGroup("/api/v1", r.protected, authService.RequireAuth()).
				WithMiddlewareNames("auth.RequireAuth"),
		)
	}

	return groups
}

Route groups are the right place for shared prefixes and group-level middleware.

Route Naming

Use REST-ish paths that describe resources. Let HTTP methods carry the action:

text
GET  /api/v1/users/:id
POST /api/v1/reports

Avoid RPC-style paths such as /api/v1/get-user for normal resource operations. See Naming Conventions for the full naming map.

Listing Routes

Use:

bash
forj route:list

For an additional app, prefix the command with the app name:

bash
forj admin route:list

Use route:list as the source of truth for what the App registered. Do not rely only on startup logs.

Framework Routes

The HTTP runtime also registers framework-owned routes such as:

  • /-/health
  • /-/ready
  • /swagger
  • /swagger/doc.json
  • /metrics when metrics are enabled
  • Lighthouse routes when Lighthouse is enabled

Do not add application behavior by editing framework route registration.

Common Mistakes

Common mistakes

  • Do not scatter route registration across unrelated packages.
  • Do not bypass web routes with low-level HTTP setup in normal App code.
  • Do not put business logic in route registration.
  • Do not make route paths depend on runtime topology.
  • Do not treat framework routes as application-owned endpoints.

Next Steps