Skip to main content

The pieces

ApivalkConfiguration is the single object that holds the runtime wiring. Apivalk (the entry point) takes one and does nothing else — so everything you do to customise the framework happens on the configuration. A full constructor looks like this:

Minimal bootstrap

The smallest working index.php:
That’s a complete, public-only API. Hitting a configured GET /health route goes through Validate → Controller and returns JSON.

Adding authentication

AuthenticationMiddleware populates the identity; SecurityMiddleware enforces it. Always add them in that order:
See how to authenticate with JWT and how to authenticate with a custom API key.

Adding a PSR-11 container

If you pass a container, Apivalk uses it to build controllers — so constructor injection works:
Without a container the controller factory falls back to new $controllerClass(), so constructor-less controllers still work.

Adding a logger

Apivalk accepts any PSR-3 logger and exposes it via $apivalk->getLogger():

Localization

See the full Localization reference. The short version:
If you pass nothing, Apivalk defaults to Locale::en().

Middleware order (LIFO onion)

MiddlewareStack::handle() reverses the list, so the last middleware you add() is the innermost layer (closest to the controller). That means:
  • RequestValidationMiddleware runs as close to the controller as possible → add it last.
  • AuthenticationMiddleware must run before SecurityMiddleware (authentication before authorization) → add Authentication first, Security second.
  • RateLimitMiddleware usually runs before anything expensive → add early.
The recommended order is:

How to extend your configuration

A raw index.php with every middleware listed inline is fine for a small service but gets noisy fast. Grow into one of these patterns:

1. Move bootstrapping into an application class

Wrap everything the request-scoped entry point needs in a factory:
Your public/index.php then shrinks to:
Tests, CLI tools, and the OpenAPI generator script can reuse the same factory so routing/middleware definitions never drift.

2. Register middlewares through the container

Pull middlewares out of the bootstrap file and into the container as first-class services. That lets them take dependencies (database, PSR-3 logger, cache, feature flags) without you constructing them by hand:
And in the bootstrap:
This is also how you add your own middlewares — see Write a custom middleware.

3. Swap the renderer or exception handler per environment

JsonRenderer and ApivalkExceptionHandler::handle are defaults, not requirements. Both are constructor arguments:
Ship your own RendererInterface implementation when you need non-JSON output or custom envelopes; ship your own exception handler when you need to integrate with Sentry / Datadog / Bugsnag.

4. Keep framework bridges thin

If you’re running inside Laravel or Symfony, use the framework bridges rather than re-implementing wiring. The bridges do the ApivalkBootstrap work and hand you an Apivalk instance tuned for that framework’s lifecycle.