What is the difference between IActionResult, ActionResult<T> and returning T directly?
Quick Answer
Return T directly for the simplest case — the framework serialises it as 200. Return IActionResult when the action produces different status codes (Ok, NotFound, BadRequest) but you lose the response type for tooling. ActionResult<T> is the best of both — you can return either a T or a status result, and Swagger infers the success type.
If the action always succeeds, I just return the object and let the framework serialise it. The moment I need more than one outcome — found versus not found — I need a result type. IActionResult works but it's opaque; Swagger can't tell what the 200 body looks like. ActionResult<T> fixes that: the signature declares the success type, and I can still return NotFound() or return BadRequest(). I annotate the other status codes with ProducesResponseType so the generated OpenAPI is accurate. In minimal APIs the equivalent is the typed Results<Ok<T>, NotFound> union.
public ActionResult<Order> Get(int id) => _svc.Find(id) is {} o ? o : NotFound();.- Add
[ProducesResponseType(StatusCodes.Status404NotFound)]for the non-success codes so OpenAPI is complete. - Minimal APIs use
Results<Ok<Order>, NotFound>(typed results) for the same benefit. [ApiController]auto-converts invalid model state to a 400ProblemDetailsbefore your code runs.