This repository is a greenfield reference implementation for a use-case-driven, contract-first API built with plain Symfony.
The goal is to validate patterns that could realistically transfer into Shopware later:
- hand-authored OpenAPI as the public contract
- explicit DTO mapping instead of entity exposure
- thin controllers and use-case handlers
- contract testing against the OpenAPI document
- minimal runtime magic and minimal framework lock-in
- audience-shaped API surfaces for store and admin use cases
The implemented slice is still intentionally small. It is large enough to test store reads, idempotent writes, one admin read flow, versioning, and contract governance, but it is not yet proof that the same discipline will feel equally comfortable across dozens of use cases. Teams should validate naming, module boundaries, shared assemblers, and OpenAPI module structure again when the surface grows.
The current reference slice is split into two explicit audiences.
Store contract API:
GET /api/store/productsGET /api/store/products/{product_id}GET /api/store/categoriesGET /api/store/categories/{category_id}POST /api/store/cartsGET /api/store/carts/{cart_id}POST /api/store/carts/{cart_id}/items
Admin contract API:
GET /api/admin/open-carts
Deprecation proof point:
GET /api/store/demo/deprecated-category/{category_id}is a demo-only deprecated endpoint that advertisesDeprecation,Sunset, and successorLinkheaders for the canonicalGET /api/store/categories/{category_id}route
Public documentation endpoints:
GET /docsGET /docs/swaggerGET /docs/deprecation-policyGET /openapi.jsonGET /openapi.yaml
Local credentials for testing:
- store:
Context-Token: storefront-context-token - admin:
admin-token - default pinned contract version:
2026-04-01
Optional request version override:
Contract-Version: 2026-03-01
This project is optimized for a Podman-based workflow.
Start the app:
podman compose up -d appLoad fixtures:
podman compose run --rm app php bin/console app:fixtures:load --forceOpen the docs:
Example requests:
curl -H 'Context-Token: storefront-context-token' \
'http://localhost:8001/api/store/products?sort=price'
curl -H 'Authorization: Bearer admin-token' \
'http://localhost:8001/api/admin/open-carts'
curl -H 'Context-Token: storefront-context-token' \
-H 'Contract-Version: 2026-03-01' \
'http://localhost:8001/api/store/products?page_size=1'Run everything with:
./bin/test-everythingThat script will:
- install Node dependencies if needed
- lint and publish the OpenAPI artifacts
- detect breaking OpenAPI changes against the committed baseline
- validate versioned frozen specs and changelogs
- prove machine-consumability with
@shopware/api-gen - start the app
- reload fixtures
- run PHPStan
- run Rector in dry-run mode
- run php-cs-fixer in dry-run mode
- run PHPUnit
- run phpbench benchmarks
- hit the main store/admin HTTP flows end to end
Lint Markdown docs with:
npm run docs:lint:markdownThe canonical OpenAPI source is:
The source is split into reusable JSON modules under:
openapi/src/pathsopenapi/src/test-metadata/pathsopenapi/src/components/schemasopenapi/src/components/responsesopenapi/src/components/parametersopenapi/src/components/requestBodiesopenapi/src/components/headers
Build-time tooling:
- Redocly CLI for linting and bundling
- static Redoc output as the default reviewer-facing docs
- local Swagger UI assets built from
swagger-ui-distfor interactive testing - custom structure checks to prevent duplicate schema shapes and inline complex schemas
@shopware/api-genproof generation for audience-scoped TypeScript output
Relevant commands:
npm install
npm run openapi:lint
npm run openapi:build:test-spec
npm run openapi:check:structure
npm run openapi:check:versioning
npm run openapi:diff
npm run openapi:publish
npm run openapi:proof:api-genPublished build artifacts:
openapi/baseline/openapi.jsonopenapi/build/openapi.yamlopenapi/build/openapi.jsonopenapi/build/openapi.test.jsonopenapi/build/versions/2026-03-01/openapi.jsonopenapi/build/versions/2026-04-01/openapi.jsonpublic/openapi/openapi.yamlpublic/openapi/openapi.jsonpublic/openapi/versions/2026-03-01/openapi.jsonpublic/openapi/versions/2026-04-01/openapi.jsonpublic/docs/index.htmlpublic/docs/swagger/index.htmlproofs/api-gen/2026-04-01/store/api-types/storeApiTypes.d.tsproofs/api-gen/2026-04-01/admin/api-types/adminApiTypes.d.tsproofs/api-gen/2026-03-01/store/api-types/storeApiTypes.d.tsproofs/api-gen/2026-03-01/admin/api-types/adminApiTypes.d.ts
The public docs and published raw spec are built from openapi/build/openapi.json.
Contract tests use the internal merged artifact at
openapi/build/openapi.test.json, which adds sidecar x-test-* metadata back
for the test harness without exposing that control metadata in the public spec.
PHP quality:
podman compose run --rm app composer phpstanpodman compose run --rm app composer rectorpodman compose run --rm app composer cspodman compose run --rm app php bin/phpunitpodman compose run --rm app composer phpbench
Architecture rules are enforced through PHPat in the PHPStan run.
Hosted quality:
- GitHub Actions workflow: /.github/workflows/ci.yml
- OpenAPI breaking-change detection via
npm run openapi:diff
Repository-friendly request collection:
Performance scenarios:
- Plan
- Architecture Overview
- Status Checklist
- OpenAPI Structure
- Adding a Use Case
- Contract Test Metadata
- Machine Consumability
- Versioning Policy
- Performance Plan
- Performance Results
- RFC 9865 Cursor Pagination
- Shopware Transfer Playbook
- Shopware Side-by-Side
- What We Learned
- Deprecation Policy
- Error Catalog
- ADR 0001
- ADR 0002
- ADR 0003
- ADR 0004
- ADR 0005
- ADR 0006
- ADR 0007
- ADR 0008
- ADR 0009