Skip to content

Return Types

Controller-based queries support various data types for return values, giving you flexibility in how you structure your API responses.

Return a single instance of your read model:

[HttpGet("{id}")]
public DebitAccount GetAccount(AccountId id)
{
return _collection.Find(a => a.Id == id).FirstOrDefault();
}
[HttpGet]
public IEnumerable<DebitAccount> GetAccounts()
{
return _collection.Find(_ => true).ToList();
}
[HttpGet]
public List<DebitAccount> GetAccountsList()
{
return _collection.Find(_ => true).ToList();
}
[HttpGet]
public DebitAccount[] GetAccountsArray()
{
return _collection.Find(_ => true).ToArray();
}

For more control over the response metadata, you can return QueryResult:

[HttpGet]
public QueryResult GetAccountsWithMetadata()
{
var accounts = _collection.Find(_ => true).ToList();
return new QueryResult
{
Data = accounts,
// Additional metadata will be populated automatically
};
}

All return types can be wrapped in Task<T> for asynchronous operations:

[HttpGet]
public async Task<IEnumerable<DebitAccount>> GetAccountsAsync()
{
var result = await _collection.FindAsync(_ => true);
return result.ToList();
}
[HttpGet("{id}")]
public async Task<DebitAccount> GetAccountAsync(AccountId id)
{
var result = await _collection.FindAsync(a => a.Id == id);
return result.FirstOrDefault();
}

You can create custom types for complex query results:

public record AccountSummary(int TotalAccounts, decimal TotalBalance, decimal AverageBalance);
[HttpGet("summary")]
public AccountSummary GetAccountSummary()
{
var accounts = _collection.Find(_ => true).ToList();
return new AccountSummary(
accounts.Count,
accounts.Sum(a => a.Balance),
accounts.Count > 0 ? accounts.Average(a => a.Balance) : 0
);
}

For real-time queries, return ISubject<T> or IObservable<T>:

[HttpGet("observable")]
public ISubject<IEnumerable<DebitAccount>> GetAccountsObservable()
{
return _collection.Observe();
}

See Observable Queries for more details on real-time data streaming.

When a query might not return data, use nullable types:

[HttpGet("{id}")]
public DebitAccount? GetAccount(AccountId id)
{
return _collection.Find(a => a.Id == id).FirstOrDefault();
}
  1. Use appropriate collection types - IEnumerable<T> for most cases, List<T> when you need specific list operations
  2. Consider nullability - Use nullable types when queries might return no results
  3. Async for I/O operations - Always use async methods when dealing with database operations
  4. Custom types for complex data - Create dedicated response types for complex query results
  5. QueryResult for metadata - Use QueryResult (assigning its Data property) when you need to include additional response metadata

By default, controller-based queries wrap results in a QueryResult structure. To bypass this wrapper and return raw results, use the [AspNetResult] attribute. For more details, see Without wrappers.