Configuration Reference
Full YAML structure with every option, its type, and default value. The minimum required fields are website_name, user.class_name, and refresh_token.*.
Full Configuration
silverback_api_components:
# Required. Used in email subjects and templates.
website_name: My CWA App
# Table prefix for all bundle-managed tables. Default: '_acb_'
table_prefix: '_acb_'
# Key under which runtime metadata is nested in API responses. Default: '_metadata'
metadata_key: '_metadata'
# ─── User ────────────────────────────────────────────────────────────────
user:
# Required. Fully-qualified class name of your User entity.
class_name: App\Entity\User
email_verification:
enabled: true
# Whether new users start verified (false = must verify email)
default_value: false
# Send a verification email when a new user registers
verify_on_register: true
# Re-send verification when the user changes their email
verify_on_change: true
# Block login for unverified email addresses
deny_unverified_login: true
email:
# Query param name carrying the redirect path (optional)
redirect_path_query: ~
# Path with {{ username }} and {{ token }} placeholders
default_redirect_path: /verify-email/{{ username }}/{{ token }}
subject: Please verify your email
new_email_confirmation:
email:
redirect_path_query: ~
default_redirect_path: /confirm-new-email/{{ username }}/{{ new_email }}/{{ token }}
subject: Please confirm your new email address
# Token validity in seconds. Default: 86400 (24 hours)
request_timeout_seconds: 86400
password_reset:
email:
redirect_path_query: ~
default_redirect_path: /reset-password/{{ username }}/{{ token }}
subject: Your password reset request
# Minimum seconds between reset requests. Default: 8600
repeat_ttl_seconds: 8600
# Reset token validity in seconds. Default: 3600
request_timeout_seconds: 3600
emails:
welcome:
enabled: true
subject: 'Welcome to {{ website_name }}'
user_enabled:
enabled: true
subject: 'Your account has been enabled'
username_changed:
enabled: true
subject: 'Your username has been updated'
password_changed:
enabled: true
subject: 'Your password has been changed'
# ─── Publishable ─────────────────────────────────────────────────────────
publishable:
# Symfony expression: who can read/write draft resources and publish
permission: "is_granted('ROLE_ADMIN')"
# ─── HTTP Cache Safety ───────────────────────────────────────────────────
http_cache:
# Resource classes whose GET responses are marked `private, no-store`
# for authenticated users, so a shared cache never stores an admin's
# draft view. Any Publishable resource is treated this way automatically,
# in addition to this list. Default: [Route, ResourceManifest, ComponentPosition]
personalised_resource_classes:
- 'Silverback\ApiComponentsBundle\Entity\Core\Route'
- 'Silverback\ApiComponentsBundle\ApiResource\ResourceManifest'
- 'Silverback\ApiComponentsBundle\Entity\Core\ComponentPosition'
# ─── Built-in Components ─────────────────────────────────────────────────
enabled_components:
form: true # The Form component (Symfony FormType → JSON)
collection: true # The Collection component (resource list proxy)
# ─── Refresh Tokens ──────────────────────────────────────────────────────
refresh_token:
# Service ID of the refresh token storage handler
handler_id: silverback.api_components.refresh_token.storage.doctrine
options:
# Your RefreshToken entity class (from the Flex recipe)
class: App\Entity\RefreshToken
# Must match the cookie name in lexik_jwt_authentication.set_cookies
cookie_name: api_components
# Token lifetime in seconds. Default: 604800 (1 week)
ttl: 604800
# The user provider alias used in security.yaml
database_user_provider: database
# ─── Mercure ─────────────────────────────────────────────────────────────
mercure:
# Name of the Mercure hub (if multiple hubs; null = default hub)
hub_name: ~
cookie:
# SameSite attribute for the Mercure cookie. Default: 'strict'
samesite: '%env(JWT_COOKIE_SAMESITE)%'
# Scope subscriber JWT tokens to resources the current user can access.
# Recommended in production. Default: false (all topics subscribed).
secure_subscriptions: false
# ─── Route Security ──────────────────────────────────────────────────────
# URL pattern → Symfony security expression. Evaluated per-route.
route_security:
- { route: '/user-area*', security: "is_granted('ROLE_USER')" }
- { route: '/admin*', security: "is_granted('ROLE_ADMIN')" }
# Expression controlling who can access the route manifest
routable_security: "is_granted('ROLE_ADMIN')"
Mercure Secure Subscriptions
By default the subscriber JWT token issued at login includes every resource topic — Mercure will push updates for any resource the front-end subscribes to, regardless of the current user's access level. This is fine for public content but leaks real-time updates for resources that have server-side security expressions.
Set mercure.secure_subscriptions: true to scope the token: the bundle evaluates each API resource's security expression at token-generation time and omits topics the current user cannot access:
silverback_api_components:
mercure:
secure_subscriptions: true
Caveats:
- Security expressions that reference
object(item-level security, e.g.object.owner == user) cannot be evaluated at token time because there is no concrete entity instance. Those resource topics are always included — you cannot scope to specific items, only to the class/role level. - The token is generated at login time. If a user's roles change mid-session their subscription scope does not update until they re-authenticate.
Enable in production for any application with role-gated resources. Leave false (the default) for fully public sites.
HTTP Cache Safety
Some resources look different depending on who's asking. When an admin is signed in, GET /_/routes/{path}, the resource manifest, and any draft of a Publishable component return the draft view — the same URL a signed-out visitor uses to get the published view (the API decides from the auth cookie, not the URL). If a shared cache stored the admin's response, it could later serve that draft to the public.
The bundle prevents this at the source. On a GET/HEAD for an authenticated user, when the resource is in the affected set, a kernel.response listener overrides API Platform's default public caching:
Cache-Control: private, no-store
It also strips the long shared s-maxage from that response. Anonymous requests, and any resource not in the affected set (a Layout, say), are untouched and stay publicly cacheable — so the only variant a shared cache ever holds is the published one.
The affected set is the configured personalised_resource_classes (default Route, ResourceManifest, ComponentPosition) plus every Publishable resource, matched dynamically — you don't list your Publishable components. Subclasses of a listed class match too. Add your own resource classes if they vary by session:
silverback_api_components:
http_cache:
personalised_resource_classes:
- 'Silverback\ApiComponentsBundle\Entity\Core\Route'
- 'Silverback\ApiComponentsBundle\ApiResource\ResourceManifest'
- 'Silverback\ApiComponentsBundle\Entity\Core\ComponentPosition'
- 'App\Entity\MyPersonalisedComponent'
Vary: Cookie. Varying on the cookie would collapse the cache hit-rate for anonymous visitors (cookies churn), and it isn't needed — the unsafe responses are marked non-cacheable outright rather than cached under a cookie key. The cacheable anonymous variant carries no cookie to vary on.This header is the API-side contract a service worker relies on to cache CWA responses safely — a no-store/private response is the signal to not store it. See Progressive Web App & Offline on the front-end for the full picture.
Environment Variables
| Variable | Purpose |
|---|---|
DATABASE_URL | Doctrine connection string |
JWT_SECRET_KEY | Path to private key PEM file |
JWT_PUBLIC_KEY | Path to public key PEM file |
JWT_PASSPHRASE | Private key passphrase (.env.local only) |
JWT_COOKIE_SAMESITE | Cookie SameSite attribute (strict, lax, none) |
MERCURE_URL | Internal API → Mercure hub URL |
MERCURE_PUBLIC_URL | Browser → Mercure hub URL |
MERCURE_JWT_SECRET | Shared secret for Mercure publisher JWT |
MAILER_DSN | Symfony Mailer transport |
APP_SECRET | Symfony application secret |
Services You Can Override
All ~320 bundle services have stable string IDs in the form silverback.api_components.*. Each service's FQCN is also registered as an alias, so you can decorate using either form in config/services.yaml:
# config/services.yaml
services:
App\YourCustomFilesystemProvider:
decorates: Silverback\ApiComponentsBundle\Helper\Uploadable\FilesystemProvider
# or equivalently using the stable string ID:
App\YourCustomFilesystemProvider:
decorates: silverback.api_components.helper.uploadable.filesystem_provider
Commonly overridden services:
| Service ID | Purpose |
|---|---|
Silverback\ApiComponentsBundle\Helper\Uploadable\FilesystemProvider | Custom storage adapter resolution |
Silverback\ApiComponentsBundle\Security\UserChecker | Custom account checks on login |
silverback.api_components.jwt.authentication.success_handler | Post-login redirect behaviour |
silverback.api_components.jwt.authentication.failure_handler | Login failure response |
The string IDs follow the class namespace: Silverback\ApiComponentsBundle\Foo\BarBaz → silverback.api_components.foo.bar_baz. Inspect vendor/silverbackdan/api-components-bundle/src/Resources/config/services.php for the full list.
Data Fixtures
Use AbstractCwaScaffold and CwaFixtureBuilder to seed your database with layouts, pages, routes, components, and page data in a fluent API.
Console Commands
Symfony console commands provided by the API Components Bundle for managing users, cleaning up data, and generating component entities.