Application Lifecycle
This page explains what happens between vef.Run(...) and a live HTTP server.
Boot order
This is the canonical statement of the VEF boot pipeline. It is assembled in
bootstrap.go (vef.Run) and internal/bootmodules/bootmodules.go (the
Assemble function shared by vef.Run and the internal/apptest test
harness, so the two graphs cannot drift):
config -> datasource -> middleware -> api -> security -> event -> expression -> js -> cqrs -> cron -> redis -> lock -> mold -> storage -> sequence -> tx_memory -> outbox -> redis_stream -> inbox -> schema -> monitor -> mcp -> push -> app
datasource is a single step: it connects *sql.DB (via internal/database)
and wraps it into orm.DB (via internal/orm) in one module — there is no
separate database or orm boot step. tx_memory, outbox, redis_stream,
and inbox are the event transport submodules — the tx_memory transport
module, the outbox transport module, the redis_stream transport module, and
the inbox module — registered after sequence and before schema. js is
the shared JS engine module and push is the WebSocket push module.
Note the list order is for readability: FX resolves the actual construction order from declared dependencies, so what matters is the dependency shape —
- config feeds everything
- datasource feeds API handlers that need
orm.DB - security feeds authenticated API requests
- the event transport submodules (tx_memory, outbox, redis-stream, inbox)
build on the core
eventmodule - storage, monitor, schema, MCP, and push are all wired before the app starts listening
What vef.Run(...) actually does
vef.Run(...) wires the FX app in this order:
- installs the framework FX logger with
fx.WithLogger(newFxLogger) - adds the internal config module
- adds the internal datasource module
- appends every option returned by
bootmodules.Assemble - appends the user-provided
options... - appends
fx.Invoke(cron.StartScheduler) - appends
fx.Invoke(startApp) - appends
fx.StartTimeout(defaultTimeout) - appends
fx.StopTimeout(defaultTimeout*2) - creates the app with
fx.New(opts...) - runs it with
app.Run()
defaultTimeout is 30 * time.Second, so the default start timeout is 30s
and the default stop timeout is 60s.
Because user options are appended after bootmodules.Core(), application
modules can append group members through helpers such as
vef.ProvideAPIResource(...). Replacing a core-provided singleton usually
requires vef.Decorate(...), vef.Replace(...), or a framework replacement
helper such as vef.SupplyFileACL(...); registering a second plain
vef.Provide(...) for the same service is not an override.
Advanced modules can receive vef.Lifecycle and call Lifecycle.Append(...)
to register an fx.Hook directly; vef.StartHook, vef.StopHook, and
vef.StartStopHook are convenience constructors for those hooks.
The exact Lifecycle.Append signature is tracked in the public API index.
The internal startApp invoke appends the HTTP server lifecycle hook after the
module graph has been constructed. Its OnStart waits for
application.Start() or the start context timeout, and its OnStop calls
application.Stop().
Minimal boot example:
func main() {
vef.Run(
ivef.Module,
auth.Module,
sys.Module,
web.Module,
)
}
App startup
The application module creates a Fiber app, applies “before” middleware, mounts the API engine, then applies “after” middleware.
That is why middleware ordering in VEF has two layers:
- app-level middleware order around the whole Fiber app
- API-level middleware order inside the API engine
App-level middleware order
From the current middleware module, the common order is:
- compression (
-1000) - headers (
-900) - CORS (
-800) - body-encoding (
-750, decodes an opt-inX-Body-Encodingrequest body back to raw JSON before the guard/parse, scoped to/api) - content type (
-700, JSON/multipart guard, scoped to/api) - request ID (
-650) - request logger binding (
-600) - panic recovery (
-500) - request record logging (
-100) - API routes
- push WebSocket endpoint (order
450; the integration inbound gateway sits at400when that module is enabled) - MCP endpoint middleware (order
500) - storage proxy routes (order
900) - SPA fallback middleware (order
1000)
The important consequence is that app middleware can run even for requests that never reach the API engine.
API-level middleware order
Inside the API engine, the request chain is sorted by middleware order and currently runs as:
- auth
- contextual setup
- data permission resolution
- rate limit
- audit
- handler
This order is what gives handlers access to:
- the authenticated principal
- request-scoped
orm.DB - request-scoped logger
- resolved data permission applier
Startup hooks from built-in modules
Some modules also use lifecycle hooks:
- datasource pings the primary connection and logs the database version
- event bus starts its in-memory dispatcher
- storage initializes providers that need startup work
- app starts the HTTP server and registers a stop hook
So a successful boot means more than “Fiber started”. It means the runtime dependencies were initialized too.
Why this matters when you debug
If a VEF app fails to start, the problem is often one of these:
- config file not found
- invalid database config
- unsupported provider configuration
- constructor registration error in FX
- handler factory resolution failure
Looking at the boot sequence tells you which layer probably failed first.
Next step
Continue to Routing to see how registered resources become actual HTTP endpoints.