Skip to content
Development
Skill

/symfony-api-response

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,

From plugin
symfony-hexagonal
3510 skills2 agents1 hook

Claude Code

$ npx -y skills add aligundogdu/symfony-hexagonal-skill --skill symfony-api-response --agent claude-code

SKILL.md

symfony-api-response.SKILL.md
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"

Symfony API Response Standard

You are an expert in building standardized REST APIs within Symfony hexagonal architecture.

When to Activate

  • User creates API endpoints or controllers
  • User asks about response format or error handling
  • User needs exception handling for APIs
  • User mentions REST, JSON, or API design

Version Notes

| 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).

Standard JSON Payload

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) |

Success Response

{
    "result": {"id": "0199a3c2-...", "name": "John"},
    "error": null,
    "extra": null,
    "status": 200
}

Error Response

{
    "result": null,
    "error": {"code": "USER_NOT_FOUND", "message": "User not found"},
    "extra": null,
    "status": 404
}

Debug Mode (APP_DEBUG=true)

{
    "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).

Controller Pattern

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]);
    }
}

References

See `references/` for detailed guides:

  • `payload-schema.md` - ApiResponseTrait, request DTOs, full controller example
  • `exception-handling.md` - ApiExceptionSubscriber, domain error mapping, validation errors
Read more
@aligundogdu
Ships withsymfony-hexagonal

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).

3510 skills2 agents
Get the whole plugin

Stars on GitHub

Momentum

stars in total
35
Star history of aligundogdu/symfony-hexagonal-skill on GitHub, a chart by star-history.com

Other skills on symfony-hexagonal.

More skills from other plugins.

brainstorming
Auto-invoked

brainstorming

You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or…

@obra@obraInstall Skill
ask-matt

ask-matt

Ask which skill or flow fits your situation. A router over the skills in this repo.

skills2w309k
@mattpocock@mattpocockInstall Skill
accessibility
Auto-invoked

accessibility

Design, implement, and audit accessible UI to WCAG 2.2 Level AA across Web, iOS, and Android — semantic ARIA roles and labels,…

ecc2w293k
@affaan-m@affaan-mInstall Skill
compiler-commit

compiler-commit

Use when you want to verify compiler changes and commit with the correct convention. Runs tests, lint, and format, then commits…

react5d252k
@facebook@facebookInstall Skill

Questions about symfony-api-response.

Which coding agents can use symfony-api-response?
symfony-api-response installs in Claude Code, Codex, Cursor, Gemini CLI, GitHub Copilot, OpenCode and Windsurf with the skills CLI, using each agent's flag shown on this page. Flowy's auto-invocation, which fires a skill when your prompt matches, runs in Claude Code only.