Skip to main content
The resource-based approach is the default for CRUD. This guide is for the cases where it doesn’t fit — e.g. endpoints don’t share the same payload shape, you need custom request bodies per operation, or you need custom response envelopes. We’ll build CRUD for a Pet: Create, View, Update, Delete. That’s 4 controllers + 4 requests + 4 responses = 12 classes. (List is a natural fifth — add it the same way.)

Directory layout

Shared pieces

Everything below assumes JWT auth using the same BearerAuth scheme across all four routes — swap in your authenticator of choice.
In real code these would live inline in the controller’s getRoute() method — they’re split out here to keep the snippets compact.

Create

CreatePetRequest

CreatePetResponse

CreatePetController

View

ViewPetRequest

No request-specific properties needed — the path parameter is declared on the route.

ViewPetResponse

ViewPetController

Update

UpdatePetRequest

Only body properties live in the request class. The path parameter is declared on the route.

UpdatePetResponse

UpdatePetController

Delete

DeletePetRequest

No request-specific properties needed — the path parameter is declared on the route.

DeletePetResponse

For idempotent deletes you can return the built-in DeletedApivalkResponse (204) instead of writing your own. If you want to emit a confirmation payload, write one like the others:

DeletePetController

When to use this over a resource

Use the manual variant when:
  • Payloads differ between endpoints — e.g. Create accepts species but Update doesn’t, or the view response returns an expanded shape with joined data.
  • Responses need custom envelopes{"data": ...} is fine for resources, but a legacy API might expect {"pet": ...} with metadata at the root.
  • You want complete control over validation — per-endpoint getDocumentation() gives you surgical control over required/optional fields, descriptions, and examples.
For uniform entities (same shape across operations), prefer resources — the 12-class scaffold collapses to 6.