Add CQRS Query
When the user asks to add a new query, scaffold the following files in the correct microservice. Ask which service if ambiguous.
Arguments
{Name}-- Query name in PascalCase (e.g.,GetCustomerById,ListOrders){Service}-- Target microservice. Ask if ambiguous.
Configuration
Read cloudstack.json from the project root at the start of execution. Extract:
NAMESPACE=project.namespace(default: detect from*.slnname or first*.csprojroot namespace)SERVICES=backend.services[](default: discover fromsrc/*/directories containing.Application/subfolders)SOLUTION=backend.solutionPath(default: find*.slninsrc/)
If cloudstack.json does not exist, auto-detect by scanning the project structure.
Prerequisites
- The target microservice must exist under
src/ - The entity and its query repository interface should exist (if not, run
/add-entityfirst) - An Endpoints file should exist at
Host/Endpoints/(if not, create one)
1. Query Record (Application/Queries/{QueryName}.cs)
namespace {Namespace}.{Service}.Application.Queries;
public record {Name}Query({parameters});
For list queries, include pagination:
public record List{Entity}Query(Guid TenantId, int PageNumber = 1, int PageSize = 20, string? Search = null);
2. Response Record (Application/Contracts/)
For single entity:
public record {Name}Response({fields});
For lists, return paged response:
public record {Name}ListResponse(IReadOnlyCollection<{Name}Response> Items, int TotalCount, int PageNumber, int PageSize);
3. Handler (Application/Queries/{Name}Handler.cs)
namespace {Namespace}.{Service}.Application.Queries;
public class {Name}Handler
{
private readonly I{Entity}QueryRepository _queryRepository;
public {Name}Handler(I{Entity}QueryRepository queryRepository)
{
_queryRepository = queryRepository;
}
public async Task<Result<{Name}Response>> Handle({Name}Query query, CancellationToken cancellationToken)
{
// Use query repository (read-optimized, potentially Dapper)
// Return Result.Success or Result.Failure("NOT_FOUND", ...)
}
}
4. Query Repository Method
Add to the query repository interface in Domain:
Task<{Entity}?> GetByIdAsync({Entity}Id id, CancellationToken cancellationToken = default);
Implement in Infrastructure using EF Core or Dapper for read-optimized queries.
5. Endpoint (add to existing Host/Endpoints/{Domain}Endpoints.cs)
group.MapGet("/{route}", {Name})
.WithName("{Name}")
.WithSummary("...")
.Produces<{Name}Response>(StatusCodes.Status200OK)
.ProducesProblem(StatusCodes.Status404NotFound);
private static async Task<IResult> {Name}(
[FromRoute] Guid id,
[FromServices] {Name}Handler handler,
CancellationToken cancellationToken)
{
var query = new {Name}Query(id);
var result = await handler.Handle(query, cancellationToken);
return result.ToHttpResult();
}
For list endpoints, use [FromQuery] for pagination:
[FromQuery] int pageNumber = 1, [FromQuery] int pageSize = 20, [FromQuery] string? search = null
Checklist
- Query record is immutable
- Uses query repository (read side), not command repository
- Handler returns
Result<T> - Endpoint has OpenAPI metadata
- Pagination supported for list queries
- CancellationToken propagated
Output
After scaffolding, report:
## Scaffolded: {Name}Query
Files created/modified:
- `src/{Service}/{Service}.Application/Queries/{Name}Query.cs`
- `src/{Service}/{Service}.Application/Queries/{Name}Handler.cs`
- `src/{Service}/{Service}.Application/Contracts/{Name}Response.cs`
- `src/{Service}/{Service}.Host/Endpoints/{Domain}Endpoints.cs` (modified)
Next: Run `/run-tests` to verify, or `/add-feature` to create the frontend calling this query.
Error Handling
- Endpoints file doesn't exist: Create a new
{Domain}Endpoints.cswith the route group boilerplate. - Query repository interface missing: Create it in the Domain layer alongside the command repository.
- Entity doesn't exist: Suggest running
/add-entityfirst before proceeding.
Related Skills
/add-commandif you also need a write endpoint for the same resource/add-entityif the entity doesn't exist yet/add-featureto create the frontend feature that calls this query