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.

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

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

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 framework automatically collects operations from all embedded OperationsProvider implementations.

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
PermToken string // Required permission token
RateLimit *RateLimitConfig // Rate limiting config
Handler any // Business logic 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]().PermToken("sys:user:query"),
Create: crud.NewCreate[User, UserParams]().PermToken("sys:user:create"),
Update: crud.NewUpdate[User, UserParams]().PermToken("sys:user:update"),
Delete: crud.NewDelete[User]().PermToken("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,
PermToken: "sys:user:reset_password",
},
),
)

Identifier

Every operation has a unique Identifier:

type Identifier struct {
Resource string // e.g., "sys/user"
Action string // e.g., "create"
Version string // e.g., "v1"
}

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

Request / Params / Meta

Request

The unified API request structure:

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

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.GetParam("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.GetMeta("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", or custom
Options map[string]any // Strategy-specific options
}

Built-in Auth Strategies

StrategyConstantDescription
Noneapi.AuthStrategyNoneNo authentication (public)
Bearerapi.AuthStrategyBearerBearer token authentication
Signatureapi.AuthStrategySignatureRequest signature authentication

Helper Functions

api.Public()        // AuthConfig with strategy "none"
api.BearerAuth() // AuthConfig with strategy "bearer"
api.SignatureAuth() // AuthConfig with strategy "signature"

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]().PermToken("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)

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

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

Next Step