Your First Endpoint

MapGet pairs a URL with a method. Return a string for text/plain, any other object for JSON; both get status 200.

SyntaxRouteHandlerBuilder MapGet(this IEndpointRouteBuilder endpoints, string pattern, Delegate handler)

An endpoint is a URL pattern paired with the method that answers it. MapGet creates one for the HTTP GET method, which is what a browser uses to fetch something.

Its signature is RouteHandlerBuilder MapGet(this IEndpointRouteBuilder endpoints, string pattern, Delegate handler). The second argument is a Delegate, which means any method of any shape is accepted: a lambda written inline, a named local function, or a method on a class. The method you pass is called the route handler.

What comes back

Handler returnsContent-TypeStatus
stringtext/plain200
any other typeapplication/json200
IResultwhatever the result chooseswhatever it chooses

JSON serialisation uses the web defaults, which lower-case the first letter of each property name. A handler returning new { Message = "Hello World" } sends {"message":"Hello World"}.

Shorthand and longhand

Returning a value is shorthand. TypedResults.Ok(value) is the same response written out: the same JSON body with the same 200 status. Reach for the TypedResults form when the status code is not 200, or when the handler has to pick between two outcomes.

Example

Example · csharp
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// A string handler: the text is written to the body unchanged.
app.MapGet("/ping", () => "pong");

// Any other type is serialised to JSON, with camelCased property names.
app.MapGet("/version", () => new { Name = "orders", Major = 2, Minor = 7 });

// A record works the same way, and gives the response a named shape.
app.MapGet("/health", () => new HealthReport("ok", 3));

// TypedResults.Ok is the explicit form of the line above: identical body,
// identical 200 status, but the status is now visible in the code.
app.MapGet("/health-explicit", () => TypedResults.Ok(new HealthReport("ok", 3)));

app.Run();

// Top-level statements must come first; type declarations follow them.
record HealthReport(string Status, int Checks);

When to use it

  • Expose a read-only JSON endpoint over data the service already has in memory.
  • Return 404 or 204 deliberately instead of letting every response be 200.
  • Check quickly whether a deployed service is reachable, with a one-line /ping endpoint.

More examples

Several methods on one path, with real status codes

A GET for /flag returns 200 with {"enabled":true}. A PUT returns 204 with an empty body. A POST returns 405 Method Not Allowed, which the framework produces because the path matched but no endpoint claimed that method. Note the last line: TypedResults.Ok of a string still serialises as JSON, so the body is the quoted text "GET, PUT, DELETE", not bare text.

Example · csharp
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// One path, three HTTP methods. Each Map call is a separate endpoint.
app.MapGet("/flag", () => TypedResults.Ok(new { Enabled = true }));

// 204 No Content: success with nothing to send back.
app.MapPut("/flag", () => TypedResults.NoContent());

// 404 Not Found, chosen deliberately rather than by accident.
app.MapDelete("/flag", () => TypedResults.NotFound());

// MapMethods covers verbs with no dedicated helper. ["OPTIONS"] is the
// C# 12 collection expression for new[] { "OPTIONS" }; same array.
app.MapMethods("/flag", ["OPTIONS"], () => TypedResults.Ok("GET, PUT, DELETE"));

A collection becomes a JSON array

GET /products returns [{"id":1,"name":"Mug","price":9.5},{"id":2,"name":"Notebook","price":4.25}]. decimal is written as a JSON number, not a string, which is what callers expect for money. Returning the array directly is the normal shape for a read-only list endpoint; wrapping it in an object becomes worthwhile once the response needs a page count beside the items.

Example · csharp
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// [ ... ] is a collection expression. The longhand is
// new Product[] { new Product(1, "Mug", 9.5m), ... } and means the same.
Product[] catalogue =
[
    new Product(1, "Mug", 9.5m),
    new Product(2, "Notebook", 4.25m)
];

app.MapGet("/products", () => catalogue);

app.Run();

record Product(int Id, string Name, decimal Price);

A named method instead of a lambda

Lambdas are fine for one-liners and get unreadable past about five lines. Pulling the body out into a named method keeps Program.cs as a readable list of routes, and the name shows up in stack traces. This is the step that leads to putting handlers in their own file once there are more than a handful.

Example · csharp
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

// The handler parameter is a Delegate, so a named method fits as well as a
// lambda. A method written at the top level is a local function.
string Pong()
{
    return "pong";
}

app.MapGet("/ping", Pong);

app.Run();

Discussion

  • Be the first to comment on this lesson.