In the previous modules, we introduced many great improvements to our application and now we’ll explore how to better structure and manage our minimal endpoints. So far, we’ve had all of our endpoints in the Program
class – this is fine for many small applications but as our API grows, we need to do something or our code might get messy and hard to maintain.
In this module, we’ll explore how to structure our API better and remove the clutter from the Program
class while introducing better maintainability to our application.
Extension methods are a great tool in .NET that allows us to add new functionality to existing types easily. We’ll use this approach to restructure our two existing endpoints.
Let’s start by navigating to our main project and creating a folder called Endpoints
. Then inside this folder, let’s create a new class and call it ProjectEndpoints
:
public static class ProjectEndpoints { public static void RegisterProjectEndpoints( this IEndpointRouteBuilder routeBuilder) { } }
We create the ProjectEndpoints
class and mark it as static
. Then, inside it, we add the RegisterProjectEndpoints()
method. It is also static
and returns void
. It is going to be an extension method on the IEndpointRouteBuilder
interface, so we pass this as a parameter preceded by the this
keyword. The keyword is vital for the extension method to work properly.
We extend the IEndpointRouteBuilder
interface as it is used to define the contract for route building in ASP.NET Core. Furthermore, the WebApplication
class implements it and we’ll be able to use our extension method directly on our app
variable in the Program
class.
Now, let’s hop to the Program
class and cut the first two project endpoints from there.
After doing that, let’s get back to the ProjectEndpoints
class:
public static class ProjectEndpoints { public static void RegisterProjectEndpoints( this IEndpointRouteBuilder routeBuilder) { routeBuilder.MapGet("api/projects", async ( [FromServices] IServiceManager serviceManager, CancellationToken cancellationToken) => { var projects = await serviceManager.ProjectService .GetAllProjectsAsync(cancellationToken); return Results.Ok(projects); }); routeBuilder.MapGet("api/projects/{id:guid}", async ( [FromRoute] Guid id, [FromServices] IServiceManager serviceManager, CancellationToken cancellationToken) => { var project = await serviceManager.ProjectService .GetProjectAsync( id, trackChanges: false, cancellationToken); return Results.Ok(project); }); } }
Here, let’s paste both endpoints inside the RegisterProjectEndpoints()
method. The only change we need to make for them to work is to rename app
to routeBuilder
.
Now, there is one final thing left to do in the Program
class:
app.UseHttpsRedirection(); app.UseExceptionHandler(); app.RegisterProjectEndpoints();
We call the RegisterProjectEndpoints()
on our WebApplication
instance after the UseHttpsRedirection()
and UseExceptionHandler()
methods – exactly where our two endpoints used to be. With this approach, we’ve replaced over 20 lines of code with a single one!
Let’s run our API now and try to retrieve all projects via Postman by sending a GET
request:
curl --location 'https://localhost:5001/api/projects'
As expected, we get the same result we did before – we still have the same endpoints, they are just structured differently.
So, as we can see, extension methods are a great way to maintain minimal API endpoints more elegantly.
Next, we’ll explore how reflection can help us achieve the same result.