phil / api-contract-bundle
Both sides of an unversioned HTTP API contract for Symfony, on phil/api-contract and phil/idempotency: exceptions as problem details, #[Idempotent] controllers with keys in Doctrine or a cache pool, #[DeprecatedEndpoint] controllers, the deprecations of called APIs recorded and checked in CI, and it
Package info
github.com/parler-haut-interagir-librement/api-contract-bundle
Type:symfony-bundle
pkg:composer/phil/api-contract-bundle
Requires
- php: >=8.5
- ext-uri: *
- phil/api-contract: ^0.1@dev
- phil/idempotency: ^0.1@dev
- psr/clock: ^1.0
- symfony/config: ^8.2
- symfony/dependency-injection: ^8.2
- symfony/http-foundation: ^8.2
- symfony/http-kernel: ^8.2
Requires (Dev)
- doctrine/dbal: ^4.5
- doctrine/doctrine-bundle: ^3.3
- doctrine/orm: ^3.7
- phpunit/phpunit: ^13.4
- psr/cache: ^3.0
- psr/log: ^3.0
- symfony/cache: ^8.2
- symfony/clock: ^8.2
- symfony/console: ^8.2
- symfony/event-dispatcher: ^8.2
- symfony/framework-bundle: ^8.2
- symfony/http-client: ^8.2
- symfony/http-client-contracts: ^3.6
- symfony/lock: ^8.2
- symfony/routing: ^8.2
- symfony/security-core: ^8.2
- symfony/yaml: ^8.2
Suggests
- doctrine/dbal: To keep the idempotency keys in a table
- symfony/console: For phil:api-contract:deprecations and phil:api-contract:verify
- symfony/http-client: To record the deprecations of the APIs this application calls
- symfony/lock: To keep the idempotency keys in a cache pool
- symfony/security-core: To scope idempotency keys by user
- symfony/yaml: To verify an OpenAPI document written in YAML
Provides
None
Conflicts
None
Replaces
None
This package is auto-updated.
Last update: 2026-11-12 23:52:40 UTC
README
Problem details, replayed idempotent requests, deprecation notices and OpenAPI verification, wired into a Symfony application.
phil/api-contract-bundle brings both sides of an unversioned HTTP API contract into Symfony. As a server, it answers
exceptions as RFC 9457 problem details. Controllers marked #[Idempotent] run once per key, through the guard of
phil/idempotency, and replay their responses. Controllers marked #[DeprecatedEndpoint] announce Deprecation and
Sunset. As a client, it records the deprecations of the APIs the application calls, logs them once a day, and lists
them with a command that fails in CI while one has a sunset. A second command verifies the application's OpenAPI
document against a level of rules and fails in CI when a finding reaches the chosen severity.
It is for Symfony applications that publish a REST and JSON API without version numbers, or call APIs that announce
their own retirement. The rules themselves live in phil/api-contract and phil/idempotency, plain PHP libraries.
Use those libraries alone outside Symfony, or when you want the values and decisions without the wiring.
The bundle plugs into the HTTP kernel through listeners on its controller, response and exception events, and wraps Symfony's HTTP client. Doctrine DBAL, or a cache pool under a Symfony lock, keeps the idempotency keys, Symfony Security scopes them by user, the Console component runs the two commands, and the Yaml component reads a YAML document; each of these is optional. The design implements the library's ports with adapters for each of these components.
Status
| Part | State |
|---|---|
ProblemDetailsListener, ProblemException, ProblemDetailsResponse |
Implemented and tested |
#[Idempotent], IdempotencyListener, CallerScopeResolver |
Implemented and tested |
The idempotency store of phil/idempotency on a Doctrine connection or a cache pool, the schema listener |
Implemented and tested |
#[DeprecatedEndpoint], DeprecationListener |
Implemented and tested |
DeprecationObservingHttpClient, CacheObservedDeprecations, phil:api-contract:deprecations |
Implemented and tested |
phil:api-contract:verify, the verifier service, the application's own rules by tag |
Implemented and tested |
Documentation
| Document | What it covers |
|---|---|
| Installation | Requirements, registering, the table. |
| Configuration | Every key, the services, replacing a port. |
| Problems | Exceptions as problem details. |
| Idempotency | #[Idempotent], the answers and their problems, the scope, the store. |
| Deprecations | #[DeprecatedEndpoint] as a server; the HTTP client, the record and the command as a client. |
| Verification | phil:api-contract:verify, its options and exit codes, the application's own rules, CI. |
| Extension points | What an application implements, configures, replaces or relies on. |
| Testing | How the bundle is tested. |
| Reference | Every type of the bundle. |
The contract itself — problem members, headers, the levels and rules of verification — is documented with
phil/api-contract, and the keys, fingerprints and decision with phil/idempotency.
Quick start
phil_api_contract: problem_details: paths: ['^/api/'] verification: document: '%kernel.project_dir%/openapi.yaml'
#[Route('/api/orders', methods: ['POST'])] #[Idempotent] public function create(Request $request): Response { /* runs once per key */ } #[Route('/api/v0/orders', methods: ['GET'])] #[DeprecatedEndpoint(deprecatedAt: '2026-11-01', sunsetAt: '2027-05-01', link: 'https://example.com/changes')] public function legacy(): Response { /* … */ } throw ProblemException::of(409, 'This order was already shipped.');
bin/console phil:api-contract:verify --level=strict
Requirements
PHP 8.5 or later; phil/api-contract 0.1; psr/clock 1.0; Symfony 8.2's dependency injection, config, HTTP
foundation and HTTP kernel. Doctrine DBAL and ORM with DoctrineBundle, Symfony's HTTP client, a PSR-6 cache, PSR-3,
Symfony Console, Security and Yaml are optional.
Contributing
Contributions are welcome on the PHIL monorepo: read the contribution guide and the code of conduct. Report a vulnerability privately, as the security policy explains, never in a public issue.
License
MIT, © Philippe Gamache, doing business as Le Gnome Archiviste; a Parler Haut Interagir Librement project. See LICENSE.