- Home
- .NET tutorials
- TypedResults or Results for Minimal API responses?
TypedResults or Results for Minimal API responses?
Published: Monday 25 August 2025
I found out that you can use either TypedResults or Results in minimal API responses. I wondered why they were two of them and importantly which one should we be using?
// ProductDto.cs
public record ProductDto(int Id, string Name);
app.MapGet("/product/results",
() => Results.Ok(new ProductDto(1, "Watch")));
app.MapGet("/product/typed-results",
() => TypedResults.Ok(new ProductDto(1, "Watch")));
I was confused. The only difference is the name of the type. But then I discovered something when looking at the OpenAPI documentation in more detail:
...
"paths": {
"/product/results": {
"get": {
"tags": [
"RoundTheCode.MinimalApi"
],
"responses": {
"200": {
"description": "OK"
}
}
}
},
"/product/typed-results": {
"get": {
"tags": [
"RoundTheCode.MinimalApi"
],
"responses": {
"200": {
"description": "OK",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ProductDto"
}
}
}
}
}
}
}
},
"components": {
"schemas": {
"ProductDto": {
"required": [
"id",
"name"
],
"type": "object",
"properties": {
"id": {
"type": "integer",
"format": "int32"
},
"name": {
"type": "string"
}
}
}
}
},
...
The TypedResults path specifies that the content comes from the ProductDto. The Results path doesn't do that.
The Results type doesn't return any metadata for the response
The TypedResults type does return any metadata for the response
So with that in mind it would recommended to use the TypedResults type. But we've only tried it with returning an object type. What about some of the other response types? Do they do the same thing with Results and TypedResults?
The different return types
I was overwhelmed by the number of different return types there are. As our time is precious, I've gone through the ones that I think are the most common:
JSON
This returns a response in a JSON format. You can use anonymous types like in this snippet:
// Program.cs
app.MapGet("/json", () => TypedResults.Json(new { Id = 1 }));
By default, minimal API apps will use the web defaults for returning JSON. If you wish to change it, you can call ConfigureHttpJsonOptions extension method in the IServiceCollection type.
// Program.cs
builder.Services.ConfigureHttpJsonOptions(options => {
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.SerializerOptions.WriteIndented = true;
options.SerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull;
});
There are a large number of JSON options that you can configure. You can also configure JSON serialisation for an individual route.
JsonSerializerOptions class, and then add it to the response.
/json-custom route:
// Program.cs
app.MapGet("/json-custom",
() => Results.Json(new { Id = 1 }, new JsonSerializerOptions
{
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseUpper
}));
Custom status code
I found that a number of status codes have their own method like Ok and NotFound. But if you find that one doesn't exist, you can use the StatusCode method to return it, like this one:
// Program.cs
app.MapGet("/im-a-teapot", () => TypedResults.StatusCode(StatusCodes.Status418ImATeapot));
Internal server error
Hopefully you won't need this one. But If you ever have to return an internal server error and a message as part of the response, you can call the InternalServerError method:
// Program.cs
app.MapGet("/internal-server-error",
() => TypedResults.InternalServerError("Broken"));
Problem and ValidationProblem
When there is a problem with your endpoint, you can use either the Problem or ValidationProblem extension method to return a response:
// Program.cs
app.MapGet("/problem",
() => TypedResults.Problem("There is an error", statusCode: StatusCodes.Status403Forbidden));
app.MapGet("/validation-problem",
() => {
var problems = new Dictionary<string, string[]> { { "Id", ["Missing"] } };
return TypedResults.ValidationProblem(problems);
});
ValidationProblem will always return a 400 response so this is ideal for form requests that have errors. With the Problem extension method, you can choose which status code to return.
Text
Returning plain text responses is done by using the Text extension method. I've used this in the past to return dynamic robots.txt files.
// Program.cs
app.MapGet("/robots.txt", () => TypedResults.Text("User-agent: *"));
Redirect
The Redirect extension method allows you to redirect a route to another route. And you can choose whether to make it permanent by setting the permanent parameter. If it's set to true, it will return a 301 response. Otherwise, it will return a 302 response.
// Program.cs
app.MapGet("/permanent-redirect", () => TypedResults.Redirect("/", permanent: true));
app.MapGet("/temporary-redirect", () => TypedResults.Redirect("/", permanent: false));
File
If you want to download a file, you can return the File extension method.
Results or TypedResults. If you use Results, you can specify the file name. But if you use TypedResults, you need to download the file into a stream and then return that stream as part of the response.
// Program.cs
app.MapGet("/file", () =>
{
var filePath = $"{AppDomain.CurrentDomain.BaseDirectory}/File/RoundTheCode.txt";
return Results.File(
filePath,
contentType: "text/plain",
fileDownloadName: "RoundTheCode.txt"
);
});
app.MapGet("/file-from-stream", async() =>
{
var filePath = $"{AppDomain.CurrentDomain.BaseDirectory}/File/RoundTheCode.txt";
return TypedResults.File(
await File.ReadAllBytesAsync(filePath),
contentType: "text/plain",
fileDownloadName: "RoundTheCode.txt"
);
});
Returning different response types
I needed to return different response types for a route. I wanted to throw a not found error if I couldn't find an ID, otherwise I would return a ProductDto type as an OK response.
Results type is going to be returned. But you can also set generic types within that. This examples specifies that the endpoint will return an Ok type with a ProductDto type, as well as a NotFound type.
// Program.cs
app.MapGet("/different-responses/{id:int}",
Results<Ok<ProductDto>, NotFound>
(int id) =>
{
if (id > 1)
{
return TypedResults.NotFound();
}
return TypedResults.Ok(new ProductDto(id, "Abc"));
});
Manually specifying the response types
Even with using TypedResults, there are some response types that can have a number of status codes returned. For example, the Redirect response type can return a 301 or 302 response code. As a result, the Scalar document will output an expected 200 response if there is more than one. This also happens with the StatusCode and Problem response types that we covered.
Produces extension method. And you can call it multiple times if you have more than one expected response code:
app.MapGet("/permanent-redirect", () => TypedResults.Redirect("/", permanent: true))
.Produces(StatusCodes.Status301MovedPermanently);
You can also add attributes to the endpoint handler to override the endpoint summary, description and specify which response code it returns:
app.MapGet("/permanent-redirect", () => TypedResults.Redirect("/", permanent: true))
.Produces(StatusCodes.Status301MovedPermanently);
You'll see how it looks in the Scalar document when you watch this video:
I also cover TypedResults and Results in it as well as going through some of the different response types.
Final thoughts
So TypedResults or Results? Well it has to be TypedResults as it returns a more specific response type and it will often add the expected response code to the OpenAPI documentation.
TypedResults methods like Redirect didn't add the correct response codes to the OpenAPI schema. But that is because more than one status code could be returned.
Related tutorials