Bundle Setup
The API Components Bundle is the Symfony backend of CWA. It wires together API Platform, Doctrine ORM, LexikJWTAuthenticationBundle, Mercure, and a suite of content management abstractions so you spend your time building your app rather than infrastructure.
Prerequisites
- PHP 8.5+
- Symfony 7.4+
- Doctrine ORM
- API Platform 4.x
Installation
composer require components-web-app/api-components-bundle
components-web-app/api-components-bundle, but the PHP namespace is Silverback\ApiComponentsBundle\ — the mismatch is historical, not a typo.The Flex recipe runs automatically and creates:
src/Entity/User.php— your user entity extendingAbstractUsersrc/Entity/RefreshToken.php— the refresh token entityconfig/packages/silverback_api_components.yaml— the bundle configurationconfig/packages/security.yaml— a pre-wired security configurationconfig/jwt/— directory for JWT keys (generated in the next step)
Generate JWT Keys
The bundle uses cookie-based JWT tokens for authentication. Generate a key pair once per environment:
mkdir -p config/jwt
openssl genpkey -out config/jwt/private.pem -aes256 -algorithm rsa -pkeyopt rsa_keygen_bits:4096
openssl pkey -in config/jwt/private.pem -out config/jwt/public.pem -pubout
Add the passphrase to .env.local — never commit this file:
JWT_PASSPHRASE=your_secure_passphrase
Add the key paths to .env:
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
Core Configuration
Open config/packages/silverback_api_components.yaml. This is the configuration the CWA template app ships with:
silverback_api_components:
website_name: My CWA App
user:
class_name: App\Entity\User
email_verification:
default_value: false
verify_on_register: true
verify_on_change: true
deny_unverified_login: true
email:
redirect_path_query: null
default_redirect_path: /verify-email/{{ username }}/{{ token }}
password_reset:
email:
redirect_path_query: null
default_redirect_path: /reset-password/{{ username }}/{{ token }}
new_email_confirmation:
email:
redirect_path_query: null
default_redirect_path: /confirm-new-email/{{ username }}/{{ new_email }}/{{ token }}
publishable:
permission: "is_granted('ROLE_ADMIN')"
refresh_token:
handler_id: silverback.api_components.refresh_token.storage.doctrine
options:
class: App\Entity\RefreshToken
cookie_name: api_components
ttl: 604800 # 1 week in seconds
database_user_provider: database
user.email_verification booleans and the three default_redirect_path values have no defaults — the bundle reads them when it compiles the container. Omitting them does not raise a helpful configuration error; it silently wires the user services with null. The redirect paths are front-end routes, so keep them in step with the pages your Nuxt app actually serves.Database Setup
The bundle adds tables for layouts, pages, routes, component groups, component positions, media objects, refresh tokens, and your user and component entities.
bin/console doctrine:migrations:diff
bin/console doctrine:migrations:migrate
Review the generated migration before running it — the initial migration is sizeable.
Environment Variables
Set these in .env (public) and .env.local (secrets):
# Database
DATABASE_URL="postgresql://user:pass@localhost:5432/app?serverVersion=16&charset=utf8"
# JWT auth
JWT_SECRET_KEY=%kernel.project_dir%/config/jwt/private.pem
JWT_PUBLIC_KEY=%kernel.project_dir%/config/jwt/public.pem
JWT_PASSPHRASE=your_passphrase
# Mercure (real-time updates)
# MERCURE_URL is the internal publish URL — it must be reachable from the PHP container
MERCURE_URL=http://php.local/.well-known/mercure
# MERCURE_PUBLIC_URL is the subscribe URL the browser connects to
MERCURE_PUBLIC_URL=https://yourdomain.com/.well-known/mercure
MERCURE_JWT_SECRET=your_mercure_secret
# Email
MAILER_DSN=smtp://localhost:1025
Verifying the Install
Start your Symfony server and visit the API documentation UI — in the CWA template that is /_api/docs, since API Platform is mounted under the /_api prefix set in config/routes/api_platform.yaml. You should see every available resource listed.
The API entrypoint is the API root itself (/_api/), which returns the IRI of every resource collection. The Nuxt module fetches that entrypoint and discovers the Hydra documentation URL from its Link header.
Create your first admin user — pass --admin so the account has ROLE_ADMIN access:
bin/console silverback:api-components:user:create --admin
Follow the prompts to set username, email, and password. Without the flag the account is created with ROLE_USER only and cannot access the admin panel. Then load any fixtures you've defined:
bin/console doctrine:fixtures:load
What Gets Auto-Registered
You don't need to register these — the bundle provides them out of the box:
| Resource | Endpoint prefix | Purpose |
|---|---|---|
| Layout | /_/layouts | Outer page shell (header/footer) |
| Page | /_/pages | Individual pages with component groups |
| Route | /_/routes | URL → page/page data mapping |
| ComponentGroup | /_/component_groups | Named regions within a layout or page |
| ComponentPosition | /_/component_positions | Ordered slot assignments |
| Collection | /component/collections | Proxy to paginated resource lists |
| Form | /component/forms | Symfony form types via API |
Core bundle resources are served under the /_/ prefix, components under /component/, and page data under /page_data/. These sit below whatever routing prefix your app mounts API Platform on — /_api in the CWA template, so the full path to layouts is /_api/_/layouts.
Your custom components are registered when you create entity classes extending AbstractComponent.