Add or modify a CoreEx API controller (or Minimal API endpoint) in an *.Api host. USE FOR: scaffolding the MVC controller pair (XxxController + XxxReadController), GET/query/schema endpoints, POST create, PUT + PATCH full-entity update, DELETE, and custom business-action endpoints. Covers both exception-based and Result<T> service styles, and Minimal API as an alternative to MVC. DO NOT USE FOR: Api host setup / Program.cs (use coreex-solution-scaffolder), application services (use coreex-app...
Scanned 8/31/2026
Install to Claude Code
npx -y skills add Avanade/CoreEx --skill coreex-api --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Coreex Api?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/avanade-coreex-api)More formats (shields.io, HTML) on the badges page.
---
name: coreex-api
description: "Add or modify a CoreEx API controller (or Minimal API endpoint) in an *.Api host. USE FOR: scaffolding the MVC controller pair (XxxController + XxxReadController), GET/query/schema endpoints, POST create, PUT + PATCH full-entity update, DELETE, and custom business-action endpoints. Covers both exception-based and Result<T> service styles, and Minimal API as an alternative to MVC. DO NOT USE FOR: Api host setup / Program.cs (use coreex-solution-scaffolder), application services (use coreex-app-service), API integration tests (use coreex-test-api)."
argument-hint: "Optional: entity name, operations needed (get/query/create/update/delete/custom), exception-based or Result<T> service style, MVC or Minimal API"
tags: ["api", "controller", "mvc", "minimal-api", "webapi", "routing", "cqrs", "coreex"]
---
<!--
AI workflow asset — dual-audience notice:
- In the Avanade/CoreEx repository: this file is the authored source. Edit it here.
- In a consumer repository: this file was generated by `dotnet new coreex-ai` (or refreshed via
`dotnet new coreex-ai --force` / the `/coreex-docs-sync` skill). Do not hand-edit it directly —
propose the change upstream in Avanade/CoreEx instead, then refresh once it is released.
-->
# CoreEx: API Controller
Guides you through adding or modifying HTTP API endpoints in an `*.Api` host. Covers the MVC controller pair (mutations + reads), all standard CRUD verbs, custom business-action endpoints, and Minimal API as an alternative.
## When to Use
- Scaffold a new `{Name}Controller` + `{Name}ReadController` pair for an entity
- Add a GET by id, query, or `$query` schema endpoint
- Add a POST create (with Location header and idempotency)
- Add a PUT + PATCH full-entity update pair
- Add a DELETE endpoint
- Add a custom business-action endpoint (POST/PUT that isn't a standard create/replace)
- Convert or add a Minimal API endpoint alternative
## When Not to Use
- Api host setup and `Program.cs` composition — use `coreex-solution-scaffolder` (see [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md))
- Application service creation — use `coreex-app-service`
- API integration tests — use `coreex-test-api` (hand off once the endpoint is implemented)
- Subscriber or relay hosts — controllers do not belong there
## Quick Reference
**Clarifying questions before writing any code:**
0. Resolve `rop-enabled` (exception vs `Result<T>` service style — selects the standard vs `WithResult` WebApi helper variant) from the solution-root `AGENTS.md` **Feature Configuration** before asking the rest; only prompt for what is unrecorded and re-state resolved values for confirmation.
1. What entity / resource is being exposed? (names the controller and route)
2. Which operations? GET / Query / Create / Update / Delete / custom business action?
3. Is the application service exception-based or `Result<T>` pipeline style? (determines WebApi helper variant)
4. Is a read service (`I{Name}ReadService`) already present, or does it need to be created?
5. MVC controllers or Minimal API?
**Key rules at a glance:**
- **If Q4 answer is "needs to be created":** stop before scaffolding the controller pair and invoke `coreex-app-service` (Path C — CQRS Read Service) to create `I{Name}ReadService`/`{Name}ReadService` first. Never shortcut by adding query/collection methods to the existing `I{Name}Service`/`{Name}Service`.
- Inherit from `ControllerBase` — **never** `Controller` (that adds View support)
- **CQRS split:** `{Name}Controller` (POST/PUT/PATCH/DELETE → `I{Name}Service`) + `{Name}ReadController` (GET/query → `I{Name}ReadService`). Both use the **same route** and **same `[OpenApiTag]`** so they appear as one OpenAPI group
- All action methods return `Task<IActionResult>` via the `WebApi` helper — never `ActionResult<T>` directly
- Route parameter validation: use `.Required()` — **not** `.ThrowIfNull()` (wrong exception type → 500 not 400)
- `ro.Value.Adjust(v => v.Id = id.Required())` — bind route `id` into the deserialized body before passing to the service
- `ro.WithLocationUri(...)` — set the `Location` response header in POST 201 responses
- `[IdempotencyKey]` on every create-style POST — confirm with user; omit only if explicitly non-idempotent
- Always expose **both PUT and PATCH** for full-entity updates; specialised partial-update endpoints only on request
- Every action method takes `CancellationToken cancellationToken = default` — pass to the WebApi helper via `cancellationToken:` and to the service via the lambda's `ct`: `(ro, ct) => _service.XxxAsync(... , ct)`. Never discard with `(ro, _)`
- Exception-based service → standard helpers (`GetAsync`, `PostAsync`, `PutAsync`, …)
- `Result<T>` service → `WithResult` variants (`GetWithResultAsync`, `PostWithResultAsync`, `PutWithResultAsync`, …)
- No business logic in controllers — delegate immediately to the application service
- `[Query(supportsOrderBy: true), Paging(supportsCount: true)]` + `[HttpGet("$query")]` schema endpoint for query operations
- After adding a GET/Query endpoint, check whether this host already has GraphQL enabled (grep `Program.cs` for `AddCoreExGraphQLLite`, or check the host's `AGENTS.md` for a `**GraphQL:**` line). If enabled, offer to add a matching root via `coreex-graphql` in the same session; if not enabled, say nothing — do not offer to enable GraphQL, that is a separate explicit ask
- Once the endpoint is implemented, hand off to `coreex-test-api` to add/update its integration test — go straight there. Do not write a throwaway smoke test (a scratch test class, ad-hoc curl/`HttpClient` call, or manual `dotnet run` check) first "to see if it works" and then discard it; the real integration test authored by `coreex-test-api` runs against the real host/DB and **is** the verification step, so a preliminary pass is wasted effort that gets thrown away seconds later
For full workflow and code examples see [`references/workflow.md`](references/workflow.md).
## Key References
- [`/.github/instructions/coreex-api-controllers.instructions.md`](/.github/instructions/coreex-api-controllers.instructions.md) — authoritative conventions: MVC vs Minimal API, WebApi helpers, attributes, route parameter rules, CQRS split
- [`/.github/instructions/coreex-host-setup.instructions.md`](/.github/instructions/coreex-host-setup.instructions.md) — Api host / `Program.cs` composition (scaffolded by `coreex-solution-scaffolder`)
- Related skills: [`coreex-app-service`](../coreex-app-service/SKILL.md) (controllers delegate to it), [`coreex-test-api`](../coreex-test-api/SKILL.md) (integration tests for these endpoints), [`coreex-subscriber`](../coreex-subscriber/SKILL.md) (sibling host entry point), [`coreex-solution-scaffolder`](../coreex-solution-scaffolder/SKILL.md) (host setup), [`coreex-graphql`](../coreex-graphql/SKILL.md) (optional GraphQL-lite root alongside a query endpoint)
- Illustrative examples (CoreEx sample — not present in your project):
- [`ProductController` + `ProductReadController`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Products.Api/Controllers) — exception-based, full CRUD + query + $query
- [`BasketController` + `BasketReadController`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Shopping.Api/Controllers) — Result<T> style, custom business actions, cross-tagged nested route
- [`OrderController`](https://github.com/Avanade/CoreEx/tree/main/samples/src/Contoso.Orders.Api/Controllers) — exception-based, custom orchestration action returning 202 Accepted
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!