Skip to main content

API Package

The api package is the foundation of VEF's request handling layer. It defines the core abstractions — resources, operations, request/response types, and handler resolution — that all other packages build upon.

API Reference

The generated Public API Index is the no-omissions checklist for every exported symbol, exported field, and exported method. This guide explains the supported behavior and runtime contracts behind that surface.

API groups covered in this guide:

GroupPublic APIs
resource and kindapi.Resource, api.Kind, api.KindRPC, api.KindREST, api.ValidateActionName(action, kind) error, api.NewRPCResource(name, opts...), api.NewRESTResource(name, opts...), api.WithVersion(v), api.WithAuth(config), api.WithOperations(specs...)
engine and routing extensionapi.Engine, api.RouterStrategy, api.Middleware
operationsapi.OperationSpec, api.Operation, api.RateLimitConfig, api.OperationsProvider, api.OperationsCollector
request modelapi.Identifier, api.Request, api.Params, api.Meta, api.P, api.M
authapi.AuthConfig, api.Public(), api.BearerAuth(), api.SignatureAuth(), api.IPAuth(...), api.AuthStrategy, api.AuthStrategyRegistry
handler extensionapi.HandlerResolver, api.HandlerAdapter, api.HandlerParamResolver, api.FactoryParamResolver
audit, headers, versions, errorsapi.AuditEvent, api.SubscribeAuditEvent, api.HeaderXMetaPrefix, api.HeaderXTimestamp, api.HeaderXNonce, api.HeaderXSignature, api.HeaderXAppID, api.VersionV1, api.VersionV9, api.ErrInvalidRequestParams, api.ErrInvalidRequestMeta, api.ErrInvalidParamsType, api.ErrInvalidMetaType

Architecture

api.Engine
├── Register(resources...) — register resources
├── Mount(router) — attach to Fiber
└── Lookup(id) — find operation at runtime

api.Resource
├── Kind() — RPC or REST
├── Name() — resource path (e.g., "sys/user")
├── Version() — API version (e.g., "v1")
├── Auth() — authentication config
└── Operations() — list of OperationSpec

api.OperationSpec → api.Operation (runtime)

Resource

A Resource groups related API operations under a common path. VEF provides two resource kinds:

Creating Resources

// RPC resource — uses snake_case actions
resource := api.NewRPCResource("sys/user")

// REST resource — uses HTTP verbs
resource := api.NewRESTResource("sys/user")

// With options
resource := api.NewRPCResource("sys/user",
api.WithVersion("v2"),
api.WithAuth(api.BearerAuth()),
api.WithOperations(
api.OperationSpec{Action: "create", Handler: createHandler},
api.OperationSpec{Action: "find_page", Handler: findPageHandler},
),
)

Resource Kind

KindConstantName FormatAction FormatExample
RPCapi.KindRPCsnake_case with / separatorssnake_casesys/usercreate, find_page
RESTapi.KindRESTkebab-case with / separators<verb> or <verb> <sub-resource>sys/userget, post, get user-friends

Kind.String() returns rpc for KindRPC, rest for KindREST, and unknown for any other value.

Resource Name Rules

RuleValidInvalid
Must start with lowercase letteruser, sys/userUser, 1user
No leading/trailing slashessys/user/sys/user/
No consecutive slashessys/usersys//user
RPC: snake_case segmentssys/data_dictsys/data-dict
REST: kebab-case segmentssys/data-dictsys/data_dict

Resource Options

OptionDescription
api.WithVersion(v)Override the engine's default version (e.g., "v2")
api.WithAuth(config)Set resource-level authentication
api.WithOperations(specs...)Provide operation specs directly

api.NewRPCResource and api.NewRESTResource validate the resource name, version, and any specs passed through direct api.WithOperations(...) at construction time. They panic when validation fails. api.ValidateActionName(action, kind) is public for code that builds resources dynamically and wants to apply the same RPC/REST action validation before constructing an OperationSpec.

REST action validation accepts these lowercase method tokens: get, post, put, delete, patch, head, options, trace, connect, and all. Sub-resource paths may contain /, but each segment must use kebab-case; dynamic Fiber params such as /:id are not accepted by the public validator.

Resource Interface

type Resource interface {
Kind() Kind
Name() string
Version() string
Auth() *AuthConfig
Operations() []OperationSpec
}

When using CRUD builders, you typically embed api.Resource and CRUD providers. The built-in collectors read direct Resource.Operations() specs and anonymous embedded OperationsProvider values. Direct WithOperations(...) specs are validated by the resource constructor. During engine registration, collected operation specs must have a non-empty action; custom OperationsProvider implementations should still produce action strings that already satisfy api.ValidateActionName(...).

OperationSpec

OperationSpec is the static definition of an API endpoint:

type OperationSpec struct {
Action string // Action name (e.g., "create", "find_page")
EnableAudit bool // Enable audit logging
Timeout time.Duration // Request timeout
Public bool // No authentication required
RequiredPermission string // Required permission token (renamed from PermToken in v0.24)
RateLimit *RateLimitConfig // Rate limiting config
Handler any // Business logic handler
}

At runtime the engine materializes an Operation with final Auth and RateLimit pointers for router strategies, middleware, diagnostics, and tests. Operation and Request both embed Identifier, so Identifier.String() is promoted to Operation.String() and Request.String().

Operation defaulting rules:

FieldRuntime behavior
Actionrequired; direct WithOperations(...) specs are validated against the resource kind by the resource constructor; engine registration rejects an empty action
EnableAuditcopied directly to the runtime operation
Timeoutnon-positive values use the engine default, which is 30s unless overridden
Publictrue resolves auth to api.Public() before resource/default auth
RequiredPermissioncopied into auth options as the required permission token when non-empty
RateLimitnil uses the engine default — vef.api.rate_limit when configured, else Max=100, Period=5m (v0.38); a custom RateLimitConfig replaces the default
HandlerRPC may infer from action when omitted; REST requires an explicit handler

Using with CRUD Builders

Most operations are defined through CRUD builders rather than raw OperationSpec:

type UserResource struct {
api.Resource

crud.FindPage[User, UserSearch]
crud.Create[User, UserParams]
crud.Update[User, UserParams]
crud.Delete[User]
}

func NewUserResource() *UserResource {
return &UserResource{
Resource: api.NewRPCResource("sys/user"),
FindPage: crud.NewFindPage[User, UserSearch]().RequiredPermission("sys:user:query"),
Create: crud.NewCreate[User, UserParams]().RequiredPermission("sys:user:create"),
Update: crud.NewUpdate[User, UserParams]().RequiredPermission("sys:user:update"),
Delete: crud.NewDelete[User]().RequiredPermission("sys:user:delete"),
}
}

Using with Custom Handlers

For non-CRUD operations, use WithOperations or implement OperationsProvider:

resource := api.NewRPCResource("sys/user",
api.WithOperations(
api.OperationSpec{
Action: "reset_password",
Handler: resetPasswordHandler,
RequiredPermission: "sys:user:reset_password",
},
),
)

Identifier

Every operation has a unique Identifier:

type Identifier struct {
Resource string `json:"resource" form:"resource" validate:"required,alphanum_us_slash" label_i18n:"api_request_resource"`
Action string `json:"action" form:"action" validate:"required" label_i18n:"api_request_action"`
Version string `json:"version" form:"version" validate:"required,alphanum" label_i18n:"api_request_version"`
}

// String format: "sys/user:create:v1"
id.String()

The same Identifier fields are used by JSON RPC bodies and form RPC requests. Identifier.String() always formats as {resource}:{action}:{version}.

Request / Params / Meta

Request

The unified API request structure:

type Request struct {
Identifier
Params Params `json:"params"`
Meta Meta `json:"meta"`
}

Embed api.P in request parameter structs and api.M in metadata structs when you want handler injection to decode from params or meta explicitly.

Params

Params holds the business data of a request (the "what"):

type Params map[string]any

// Decode into a typed struct
var userParams UserParams
err := request.Params.Decode(&userParams)

// Access individual values
value, exists := request.Params["username"]

Meta

Meta holds request metadata (the "how" — pagination, sorting, format):

type Meta map[string]any

// Decode into a typed struct
var pageable page.Pageable
err := request.Meta.Decode(&pageable)

// Access individual values
value, exists := request.Meta["format"]

Params vs Meta

AspectParamsMeta
PurposeBusiness dataRequest control
Decoded intoTParams or TSearchpage.Pageable, DataOptionConfig, etc.
Examplesusername, email, departmentIdpage, size, sort, format

Authentication

AuthConfig

type AuthConfig struct {
Strategy string // "none", "bearer", "signature", "ip", or custom
Options map[string]any // Strategy-specific options
}

AuthConfig.Clone() returns a copy of the strategy/options pair. Use it when a resource or operation customizes auth without mutating shared config.

Built-in Auth Strategies

StrategyConstantDescription
Noneapi.AuthStrategyNoneNo authentication (public)
Bearerapi.AuthStrategyBearerBearer token authentication
Signatureapi.AuthStrategySignatureRequest signature authentication
IPapi.AuthStrategyIPSource-IP whitelist authentication

Helper Functions

api.Public() // AuthConfig with strategy "none"
api.BearerAuth() // AuthConfig with strategy "bearer"
api.SignatureAuth() // AuthConfig with strategy "signature"
api.IPAuth() // AuthConfig with strategy "ip" and whitelist "default"
api.IPAuth("ops") // AuthConfig with strategy "ip" and whitelist "ops"

api.IPAuth(...) accepts zero or one whitelist name. With no argument it uses api.DefaultIPWhitelist ("default"); the selected name is stored under api.AuthOptionWhitelist in AuthConfig.Options. Passing more than one name panics. The built-in IP strategy resolves the named list through security.IPWhitelistLoader; the default loader reads vef.security.ip_whitelists. All auth failures deny with security.ErrIPNotAllowed, and an empty or missing named whitelist is fail-closed rather than treated as public access. Behind a reverse proxy, configure vef.app.trusted_proxies so Fiber resolves the real client IP.

Custom authentication strategies implement api.AuthStrategy and register with vef.ProvideAuthStrategy(...) into vef:api:auth_strategies.

Auth at Resource vs Operation Level

// Resource-level: all operations use signature auth
api.NewRPCResource("external/webhook", api.WithAuth(api.SignatureAuth()))

// Operation-level: override per operation
crud.NewCreate[User, UserParams]().Public() // No auth
crud.NewFindPage[User, UserSearch]().RequiredPermission("sys:user:query") // Bearer + permission

Rate Limiting

type RateLimitConfig struct {
Max int // Maximum requests allowed
Period time.Duration // Time window
Key string // Custom rate limit key (optional)
}

Usage via CRUD builder:

crud.NewCreate[User, UserParams]().RateLimit(100, time.Minute)

The built-in rate limiter uses a sliding window, counted per node. An operation that declares no RateLimit of its own receives the engine default, which is user-configurable since v0.38:

[vef.api.rate_limit]
max = 100 # default when omitted
period = "5m" # default when omitted

An explicit RateLimitConfig with Max <= 0 disables limiting for that operation. The framework's default key includes resource, version, action, resolved client IP, and the principal ID; anonymous requests use the anonymous principal.

Operation auth is carried by Operation.Auth; a public operation resolves to api.AuthStrategyNone, while protected operations carry the selected auth strategy and options.

Engine

The Engine manages resource registration and HTTP routing:

type Engine interface {
Register(resources ...Resource) error // Add resources
Lookup(id Identifier) *Operation // Find operation at runtime
Mount(router fiber.Router) error // Attach to Fiber router
}

Handler Resolution

VEF supports flexible handler signatures through parameter injection:

// Minimal handler
func (r *UserResource) Create(ctx fiber.Ctx) error { ... }

// With auto-injected parameters
func (r *UserResource) Create(ctx fiber.Ctx, db orm.DB, principal *security.Principal) error { ... }

// With typed params
func (r *UserResource) Create(ctx fiber.Ctx, params UserParams, db orm.DB) error { ... }

Parameter Resolution Interfaces

InterfacePurpose
HandlerParamResolverResolves handler params from request context at runtime
FactoryParamResolverResolves handler params once at startup (dependency injection)
HandlerAdapterConverts any handler signature to fiber.Handler
HandlerResolverFinds the handler function on a resource

RouterStrategy is also public for custom HTTP exposure. A strategy declares which api.Kind values it can handle, receives the Fiber router in Setup, and registers each resolved operation in Route.

Error Types

ErrorMeaning
ErrEmptyResourceNameResource name is empty
ErrInvalidResourceNameResource name doesn't match naming rules
ErrResourceNameSlashResource name starts or ends with /
ErrResourceNameDoubleSlashResource name contains //
ErrInvalidResourceKindInvalid resource kind value
ErrInvalidVersionFormatVersion doesn't match v\d+ pattern
ErrEmptyActionNameAction name is empty
ErrInvalidActionNameAction doesn't match kind-specific rules
ErrInvalidParamsTypeParams.Decode target is not a pointer to struct
ErrInvalidMetaTypeMeta.Decode target is not a pointer to struct

Params.Decode and Meta.Decode require a pointer to a struct. Passing any other target returns ErrInvalidParamsType or ErrInvalidMetaType.

ErrInvalidRequestParams and ErrInvalidRequestMeta use result.ErrCodeBadRequest (1400) and HTTP status 400. They are returned when RPC form params/meta JSON or REST JSON body parsing fails.

The remaining public surface of the api package — version constants, request headers, the audit event, the auth strategy registry, operation collection types, request helpers, and handler/router extension interfaces — is ledgered symbol by symbol in the Public API Index.

Next Step