Skip to content

Handlers & Routes

The server/ layer turns a controller's business logic into HTTP endpoints. Each domain gets a handler package under server/<domain>/, individual route handlers live in their own files, and server/route.go wires everything onto the HTTP router. ftkit verify enforces the conventions below.

Handler packages

Each handler domain lives in server/<domain>/ and contains a <domain>_impl.go file — the implementation type plus its constructor. The constructor is named New<Domain>API(...) and takes a []core.CoreController slice so the framework can inject the controllers the handlers depend on.

ftkit verify (verifyHandlers) checks for the *_impl.go file, the New*API() constructor, and that the constructor accepts controllers []core.CoreController. It also checks the impl file is named after its directory (e.g. orders/orders_impl.go).

go
// server/orders/orders_impl.go
package orders

type ordersAPI struct {
    ordersCtrl *orders.OrdersController
}

func NewOrdersAPI(controllers []core.CoreController) *ordersAPI {
    // resolve the OrdersController from the injected slice
    return &ordersAPI{ /* ... */ }
}

The implementation file holds wiring only. Route handler methods — those that receive a *fiber.Ctx — must not live in *_impl.go; the verifier (verifyNoRouteHandlersInImpl) rejects any *fiber.Ctx method found there. They belong in dedicated handler files (next section).

One handler per file

Every route handler gets its own file, named {group}.{camelCase}_handler.go, containing exactly one receiver method. ftkit verify (verifyHandlerSingleFunc) enforces:

  • Naming — the file must match {group}.{operation}_handler.go.
  • Group matches the directory{group} must equal the domain folder name, so a file in server/orders/ is named orders.<operation>_handler.go.
  • Operation is camelCase{operation} must start lowercase and contain no underscores or dashes (e.g. createOrder, listOrders).
  • Exactly one method — the file must declare precisely one receiver method. Zero methods is a warning; two or more is an error.
go
// server/orders/orders.createOrder_handler.go
package orders

func (h *ordersAPI) CreateOrder(c *fiber.Ctx) error {
    // parse request, call h.ordersCtrl, write response
}

Because the file holds only a handler method, it must not declare any struct either — verifyNoDataStructs flags structs in *_handler.go files just as it does in controllers.

Routes

HTTP routing is wired in server/route.go via an initHTTPHandle() function. The verifier (verifyRouteFile) looks for:

  • initHTTPHandle() — the function that registers all routes.
  • API versioning — an api/v* prefix (e.g. api/v1).
  • Route groups — at least one .Group(...) call to scope related endpoints.
  • Handler references — routes that call handlers through the server in the form s.<Xxx>Handler.<Operation>.
go
// server/route.go
func (s *server) initHTTPHandle() {
    v1 := s.app.Group("/api/v1")

    orders := v1.Group("/orders")
    orders.Post("/", s.OrdersHandler.CreateOrder)
    orders.Get("/", s.OrdersHandler.ListOrders)
}

For the handler packages to register at startup, server/server.go must blank-import the biz package (import _ ".../biz"). ftkit verify (verifyServerStructure) checks for this blank import; without it the controllers never self-register and the handlers have nothing to wire to.