Table of Contents

Controller Based Queries

You can represent queries as regular ASP.NET Core Controller actions with HTTP GET methods.

public record DebitAccount(AccountId Id, AccountName Name, CustomerId Owner, decimal Balance);

[Route("api/accounts")]
public class Accounts : Controller
{
    readonly IMongoCollection<DebitAccount> _collection;

    public Accounts(IMongoCollection<DebitAccount> collection) => _collection = collection;

    [HttpGet]
    public IEnumerable<DebitAccount> AllAccounts() => _collection.Find(_ => true).ToList();
}

Note: This particular model represents its values as concepts - a value type encapsulation that makes us not use primitives - thus creating clearer APIs and models.

Note: If you're using the Cratis Arc proxy generator, the method name will become the query name for the generated TypeScript file and class.

Key Features

Controller-based queries provide several powerful features:

  • Standard ASP.NET Core routing and HTTP verb support
  • Flexible return types including collections, single objects, and custom response wrappers
  • Dependency injection for services and repositories
  • Query arguments via route parameters, query strings, and request bodies
  • Async support for asynchronous operations
  • Observable queries for real-time data streaming
  • Custom route templates for RESTful API design

When to Use Controller-Based Queries

Controller-based queries are ideal when you:

  • Want explicit control over HTTP routing and URL structure
  • Need to leverage existing ASP.NET Core features like filters, middleware, or custom attributes
  • Are building RESTful APIs with standard HTTP conventions
  • Want to separate query logic from your read models
  • Need complex routing scenarios with multiple parameters

Bypassing Query Result Wrappers

By default, controller-based queries return results wrapped in a QueryResult structure. If you need to return the raw result from your controller action without this wrapper, you can use the [AspNetResult] attribute. For more details, see Without wrappers.

Basic Example with Async Support

For asynchronous operations, you can return Task<T>:

[HttpGet]
public async Task<IEnumerable<DebitAccount>> AllAccountsAsync()
{
    var result = await _collection.FindAsync(_ => true);
    return result.ToList();
}

Note: The proxy generator automatically creates TypeScript types for your controller methods, making them strongly typed on the frontend as well.