symfony-cqrs-handlers
Symfony CQRS with Messenger: commands, queries, handlers, command.bus / query.bus / event.bus configuration, typed bus wrappers,…
Symfony REST API response standard {result, error, extra, status}: thin controllers, #[MapRequestPayload] / #[MapQueryString] request DTOs, ApiResponseTrait, exception subscriber that unwraps Messenger HandlerFailedException, validation error format, debug mode. Use when: API,
Claude Code
$ npx -y skills add aligundogdu/symfony-hexagonal-skill --skill symfony-api-response --agent claude-code
Codex
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-api-response -a codex -y
Cursor
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-api-response -a cursor -y
Gemini CLI
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-api-response -a gemini-cli -y
GitHub Copilot
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-api-response -a github-copilot -y
OpenCode
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-api-response -a opencode -y
Windsurf
$ npx skills add aligundogdu/symfony-hexagonal-skill --skill symfony-api-response -a windsurf -y
name: symfony-api-response
description: "Symfony REST API response standard {result, error, extra, status}: thin controllers, #[MapRequestPayload] / #[MapQueryString] request DTOs, ApiResponseTrait, exception subscriber that unwraps Messenger HandlerFailedException, validation error format, debug mode. Use when: API, endpoint, controller, response, error handling, JSON, REST, API response, exception subscriber, HTTP, request DTO"You are an expert in building standardized REST APIs within Symfony hexagonal architecture.
| Feature | Requires | Fallback | |---|---|---| | `#[MapRequestPayload]`, `#[MapQueryString]` | Symfony 6.3 | `json_decode` + `ValidatorInterface` (see validation skill) | | `Symfony\Component\Routing\Attribute\Route` | Symfony 6.2 | `Routing\Annotation\Route` | | `#[MapUploadedFile]` | Symfony 7.1 | `$request->files->get()` | | `#[MapRequestHeader]`, variadic `#[MapRequestPayload] Dto ...$items` | Symfony 8.1 | `$request->headers->get()`, `type: Dto::class` array |
Never call `$request->get()` (removed in Symfony 8).
ALL API responses use this format:
{
"result": null,
"error": null,
"extra": null,
"status": 200
}| Field | Type | Description | |-------|------|-------------| | `result` | `mixed` | Success data (null on error) | | `error` | `?object` | Error details `{code, message, details?}` (null on success) | | `extra` | `?object` | Metadata: pagination, debug info | | `status` | `int` | HTTP status code (same as the response status) |
{
"result": {"id": "0199a3c2-...", "name": "John"},
"error": null,
"extra": null,
"status": 200
}{
"result": null,
"error": {"code": "USER_NOT_FOUND", "message": "User not found"},
"extra": null,
"status": 404
}{
"result": null,
"error": {"code": "INTERNAL_ERROR", "message": "Something went wrong"},
"extra": {"debug": {"exception": "...", "file": "...", "line": 42, "trace": "..."}},
"status": 500
}In production, 5xx messages are generic: never leak exception messages or traces.
Because every response carries a body, there is no `204 No Content` in this API: successful writes without data return `200` with `"result": null` (or `202` for async commands).
Controllers are thin: map input, dispatch, format output.
namespace App\Presentation\{Module}\API;
use App\Presentation\Shared\ApiResponseTrait;
use App\Presentation\Shared\Bus\CommandBus;
use App\Presentation\Shared\Bus\QueryBus;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\HttpKernel\Attribute\MapRequestPayload;
use Symfony\Component\Routing\Attribute\Route;
use Symfony\Component\Security\Http\Attribute\IsGranted;
#[Route('/api/{module}')]
final class {Entity}Controller
{
use ApiResponseTrait;
public function __construct(
private readonly CommandBus $commandBus,
private readonly QueryBus $queryBus,
) {
}
#[Route('', methods: ['POST'])]
#[IsGranted('ROLE_{MODULE}_CREATE')] // ALWAYS include
public function create(#[MapRequestPayload] Create{Entity}Request $request): JsonResponse
{
$id = $this->commandBus->dispatch(new Create{Entity}(/* from $request */));
return $this->created(['id' => $id]);
}
}See `references/` for detailed guides:
A Claude Code plugin that enforces hexagonal architecture (ports & adapters) in Symfony projects. Works with both new projects (full scaffolding) and existing projects (progressive, module-by-module refactoring).
Symfony CQRS with Messenger: commands, queries, handlers, command.bus / query.bus / event.bus configuration, typed bus wrappers,…
Doctrine persistence for Symfony hexagonal architecture (ORM 2.x/3.x, DBAL 3/4, DoctrineBundle 2/3): repository adapters, XML…
Symfony DDD domain modeling in pure PHP: entities, aggregates, value objects, domain events, domain exceptions, ID generation…
Symfony hexagonal architecture (ports & adapters) setup for Symfony 6.4 LTS, 7.4 LTS and 8.x: project structure, module…
Symfony Messenger async processing and Symfony Scheduler instead of cron: transports, routing (#[AsMessage] 7.2+), retry with…
Symfony ports and adapters: port interfaces in Domain, adapter implementations in Infrastructure, dependency injection,…
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…
Ask which skill or flow fits your situation. A router over the skills in this repo.