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).
// 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 inserver/orders/is namedorders.<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.
// 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>.
// 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.