Skip to main content

Custom Handlers

CRUD builders cover a lot of standard endpoints, but real applications always need business-specific operations. VEF lets you expose custom handlers for both RPC and REST resources and inject a wide range of request-scoped or startup-scoped dependencies directly into handler signatures.

Handler Resolution Overview

Resource kindHandler sourceResolution rule
RPComitted Handleraction name is converted from snake_case to a PascalCase method name on the resource
RPCexplicit Handler: "MethodName"the named method is looked up on the resource
RPCexplicit function valuethe function is used directly
RESTexplicit Handler requiredREST does not infer handler methods from the action string

RPC Handler Resolution

For RPC resources, you can omit the explicit handler and let the framework resolve it from the action name.

Example:

type UserResource struct {
api.Resource
}

func NewUserResource() api.Resource {
return &UserResource{
Resource: api.NewRPCResource(
"sys/user",
api.WithOperations(
api.OperationSpec{Action: "ping", Public: true},
),
),
}
}

func (*UserResource) Ping(ctx fiber.Ctx) error {
return result.Ok("pong").Response(ctx)
}

RPC resolution rules:

Action nameResolved method
pingPing
find_pageFindPage
get_user_infoGetUserInfo
resolve_challengeResolveChallenge

Requirements:

  • RPC action names must use snake_case
  • inferred handler methods must use PascalCase
  • if you want to bypass inference, set Handler explicitly in api.OperationSpec

REST Handler Resolution

For REST resources, you must provide the handler explicitly. The action string defines the HTTP method and optional sub-path.

api.NewRESTResource(
"users",
api.WithOperations(
api.OperationSpec{
Action: "get",
Public: true,
Handler: "List",
},
api.OperationSpec{
Action: "post admin",
Handler: "CreateAdmin",
},
),
)

REST action format:

FormatMeaningExamples
<method>root resource routeget, post, delete
<method> <sub-resource>sub-resource routeget profile, post admin, get user-friends

Rules:

  • HTTP verbs stay lowercase
  • sub-resources use kebab-case
  • REST action strings do not infer handler method names

Supported Handler Return Shapes

Handlers may return either nothing or one error.

ShapeMeaning
func(...)no explicit error result
func(...) errorstandard handler shape

Any other return shape is invalid for a direct handler.

Supported Factory Shapes

VEF also supports startup-time handler factories. A factory returns a handler closure and optionally an error.

ShapeMeaning
func(...) func(...) errorfactory returning a handler
func(...) (func(...) error, error)factory returning a handler plus startup error
func(...) func(...)factory returning a no-error handler
func(...) (func(...), error)factory returning a no-error handler plus startup error

Factories are useful when some dependencies should be resolved once at startup instead of on every request.

Built-In Handler Parameters

The framework ships these built-in handler parameter resolvers:

Parameter typeSourceTypical use
fiber.Ctxrequest contextdirect access to Fiber request/response APIs
orm.DBrequest contextquery and transaction entry point
log.Loggerrequest contextrequest-aware logging
*security.Principalrequest contextcurrent authenticated principal
api.Paramsrequest body/query/formraw params map
api.Metarequest metaraw meta map
typed struct embedding api.Prequest paramsstrongly typed params decoding + validation
typed struct embedding api.Mrequest metastrongly typed meta decoding + validation
page.Pageablerequest metapaging metadata helper
cron.SchedulerDI containerscheduler access
event.PublisherDI containerevent publishing
mold.TransformerDI containeroutput transformation
storage.ServiceDI containerstorage access

Important decoding rules:

  • typed params and typed meta structs are automatically validated with validator.Validate(...)
  • page.Pageable is treated as a built-in meta helper
  • api.Params and api.Meta bypass typed decoding and validation

Field And Resource Injection

If a handler parameter type is not covered by built-in resolvers, VEF tries to resolve it from the resource struct itself.

Resolution order:

StepHow it works
direct field matchfinds a non-embedded field on the resource with a compatible type
tagged dive field matchsearches nested fields under api:"dive"
embedded field matchsearches anonymous embedded fields

That means custom services, repositories, or helpers can be injected by storing them as fields on the resource.

Supported Factory Parameters

Startup-time factory resolvers are narrower than request-time handler resolvers. Built-in factory parameter types include:

Parameter typeSource
orm.DBDI container
cron.SchedulerDI container
event.PublisherDI container
mold.TransformerDI container
storage.ServiceDI container

Factory parameter resolution also supports compatible fields on the resource struct.

Common Handler Shapes

Thin RPC handler

func (*UserResource) ResetPassword(
ctx fiber.Ctx,
db orm.DB,
principal *security.Principal,
params *ResetPasswordParams,
) error {
// business logic
return result.Ok().Response(ctx)
}

Raw proxy-style handler

func (*DebugResource) Echo(params api.Params, meta api.Meta) error {
return result.Ok(fiber.Map{
"params": params,
"meta": meta,
}).Response(ctx)
}

Startup-time factory

func (*UserResource) BuildReport(
db orm.DB,
) (func(ctx fiber.Ctx, params ReportParams) error, error) {
return func(ctx fiber.Ctx, params ReportParams) error {
return result.Ok().Response(ctx)
}, nil
}

When To Use Custom Handlers

Use custom handlers when:

  • the operation is not a standard CRUD action
  • the endpoint coordinates multiple services
  • the response shape is domain-specific
  • the action triggers workflows, events, or external integrations
  • the request contract is simpler to express directly than through a CRUD builder

Mixing CRUD And Custom Actions

A common real-world pattern is to combine:

  • embedded CRUD builders for standard actions
  • explicit api.OperationSpec entries for the few domain-specific actions that do not fit CRUD

This keeps one resource aligned with one business area while still allowing extra actions such as “save permissions”, “publish version”, or “find related users”.

Practical Advice

  • keep handlers thin and business-focused
  • prefer typed params structs over raw api.Params
  • use raw maps only for dynamic or proxy-style endpoints
  • use factories only when startup-time dependency shaping is actually useful
  • for REST resources, always be explicit about handler mapping
  • if a parameter can be modeled as typed api.P / api.M, prefer that over manual extraction from fiber.Ctx

Next Step

Read Routing and Parameters And Metadata for exact request shapes and decoding rules.