Skip to main content

How to Group Minimal API Endpoints with MapGroup

· 5 min read
Jagdish Kumawat
Founder @ Dewiride

When every endpoint in a minimal API starts with /api/v1/categories, the prefix gets typed five times and is easy to get wrong once. MapGroup writes it down one time. Everything in the group shares the prefix and any settings you put on the group, and groups can sit inside groups. This guide does that on a real .NET 10 API.

Prerequisites​

Step 1: See the Repetition​

Without a group, each route carries the whole path:

Program.cs, before
app.MapPost("/api/v1/categories", CreateAsync);
app.MapGet("/api/v1/categories", ListAsync);
app.MapGet("/api/v1/categories/{id:int}", GetAsync);
app.MapPut("/api/v1/categories/{id:int}", UpdateAsync);
app.MapDelete("/api/v1/categories/{id:int}", DeleteAsync);

Step 2: Create a Group​

MapGroup takes the prefix, and returns something you map routes on, the same way you do on app:

Program.cs, after
var categories = app.MapGroup("/api/v1/categories");

categories.MapPost("/", CreateAsync);
categories.MapGet("/", ListAsync);
categories.MapGet("/{id:int}", GetAsync);
categories.MapPut("/{id:int}", UpdateAsync);
categories.MapDelete("/{id:int}", DeleteAsync);

ASP.NET Core's route groups section says: "The MapGroup extension method helps organize groups of endpoints with a common prefix and reduces repetitive code." The routes stay the same, and changing the prefix now takes one edit.

Step 3: Put a Group Inside a Group​

Groups can nest, and the prefixes add up. The inventory API has one group for the version, and one per feature inside it:

src/Dewiride.Inventory.Api/Program.cs (excerpt)
// Every data endpoint lives under /api/v1.
var api = app.MapGroup("/api/v1");
api.MapCategoryEndpoints();
api.MapProductEndpoints();
src/Dewiride.Inventory.Api/Features/Categories/CategoryEndpoints.cs (excerpt)
var group = routes.MapGroup("/categories").WithTags("Categories");

group.MapPost("/", CreateAsync).WithSummary("Create a category");
group.MapGet("/", ListAsync).WithSummary("List every category, by name");
group.MapGet("/{id:int}", GetAsync).WithSummary("Get one category");
group.MapPut("/{id:int}", UpdateAsync).WithSummary("Rename a category");
group.MapDelete("/{id:int}", DeleteAsync).WithSummary("Delete a category that has no products");

How to Move Minimal API Endpoints into Their Own Class explains MapCategoryEndpoints.

Step 4: Check the Routes​

The API's OpenAPI document lists every route it ended up with:

Paths in /openapi/v1.json
[
"/api/v1/categories",
"/api/v1/categories/{id}",
"/api/v1/products",
"/api/v1/products/{id}",
"/api/v1/products/{id}/archive",
"/api/v1/products/{id}/restore"
]

A terminal with the list of paths from the OpenAPI document, all starting with /api/v1 and followed by categories or products.

Calling one shows it works at the combined path:

Response (GET /api/v1/categories)
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8

[
{
"id": 1,
"name": "Cables",
"createdAtUtc": "2026-11-04T10:48:37.000261Z"
},
{
"id": 2,
"name": "Chargers",
"createdAtUtc": "2026-11-04T10:48:38.457419Z"
}
]

Step 5: Set Something on the Whole Group​

What you call on a group applies to everything in it. .WithTags("Categories") above is one example: every category endpoint gets that tag in the OpenAPI document, so documentation pages list them together. The route groups section gives more, including RequireAuthorization and endpoint filters. Set them on the group, and an endpoint added later can't forget them.

Common Mistakes​

MistakeWhat happensDo this instead
Repeating the prefix inside the group/api/v1/categories/api/v1/categoriesUse only what comes after the prefix
Mapping on app instead of the groupThe route has no prefix, and none of the group's settingsMap on the group
One group per endpointIt adds lines and saves nothingGroup what shares a prefix or setting

FAQ​

Can a prefix have a route parameter in it?​

Yes. The section says: "Route groups also support nested groups and complex prefix patterns with route parameters and constraints." An endpoint inside can capture values from any group above it.

Can a prefix be empty?​

Yes, and the section gives the reason: "This approach can be useful for adding endpoint metadata or filters to a group of endpoints without changing the route pattern."

Conclusion​

app.MapGroup("/api/v1/categories") gives you an object to map routes on, with the prefix added for you. Groups nest, prefixes combine, and settings on a group reach every endpoint inside it, which keeps the routes in one place and the handlers easy to read.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.