TLDR; Using minimal API, you can create a Web API in just 4 lines of code by leveraging new features like top-level statements and more.
Why Minimal API
There are many reasons for wanting to create an API in a few lines of code:
- Create a prototype. Sometimes you want a quick result, a prototype, something to discuss with your colleagues. Having something up and running quickly enables you to quickly do changes to it until you get what you want.
- Progressive enhancement. You might not want all the "bells and whistles" to start with but you may need them over time. Minimal API makes it easy to gradually add what you need, when you need it.
Learn more
Check out these LEARN modules on learning to use minimal API
How is it different from a normal Web API?
There are a few differences:
- Less files. Startup.cs isn't there anymore, only Program.cs remains.
-
Top level statements and implicit global usings. Because it's using top level statements,
using
andnamespace
are gone as well, so this code:
using System;
namespace Application
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
}
}
}
is now this code:
Console.WriteLine("Hello World!");
-
Routes Your routes aren't mapped to controller classes but rather setup with a
Map[VERB]
function, like you see above withMapGet()
which takes a route and a function to invoke when said route is hit.
Your first API
To get started with minimal API, you need to make sure that .NET 6 is installed and then you can scaffold an API via the command line, like so:
dotnet new web -o MyApi -f net6.0
Once you run that, you get a folder MyApi with your API in it.
What you get is the following code in Program.cs:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.MapGet("/", () => "Hello World!");
app.Run();
To run it, type dotnet run
. A little difference here with the port is that it assumes random ports in a range rather than 5000/5001 that you may be used to. You can however configure the ports as needed. Learn more on this docs page
Explaining the parts
Ok so you have a minimal API, what's going on with the code?
Creating a builder
var builder = WebApplication.CreateBuilder(args);
On the first line you create a builder
instance. builder
has a Services
property on it, so you can add capabilities on it like Swagger Cors, Entity Framework and more. Here's an example where you set up Swagger capabilities (this needs install of the Swashbuckle NuGet to work though):
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "Todo API", Description = "Keep track of your tasks", Version = "v1" });
});
Creating the app instance
Here's the next line:
var app = builder.Build();
Here we create an app
instance. Via the app
instance, we can do things like:
- Starting the app,
app.Run()
- Configuring routes,
app.MapGet()
- Configure middleware,
app.UseSwagger()
Defining the routes
With the following code, a route and route handler is configured:
app.MapGet("/", () => "Hello World!");
The method MapGet()
sets up a new route and takes the route "/" and a route handler, a function as the second argument () => "Hello World!"
.
Starting the app
To start the app, and have it serve requests, the last thing you do is call Run()
on the app
instance like so:
app.Run();
Add routes
To add an additional route, we can type like so:
public record Pizza(int Id, string Name);
app.MapGet("/pizza", () => new Pizza(1, "Margherita"));
Now you have code that looks like so:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.MapGet("/pizza", () => new Pizza(1, "Margherita"));
app.MapGet("/", () => "Hello World!");
public record Pizza(int Id, string Name);
app.Run();
Where you to run this code, with dotnet run
and navigate to /pizza
you would get a JSON response:
{
"pizza" : {
"id" : 1,
"name" : "Margherita"
}
}
Example app
Let's take all our learnings so far and put that into an app that supports GET and POST and lets also show easily you can use query parameters:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
var pizzas = new List<Pizza>(){
new Pizza(1, "Margherita"),
new Pizza(2, "Al Tonno"),
new Pizza(3, "Pineapple"),
new Pizza(4, "Meat meat meat")
};
app.MapGet("/", () => "Hello World!");
app.MapGet("/pizzas/{id}", (int id) => pizzas.SingleOrDefault(pizzas => pizzas.Id == id));
app.MapGet("/pizzas", (int ? page, int ? pageSize) => {
if(page.HasValue && pageSize.HasValue)
{
return pizzas.Skip((page.Value -1) * pageSize.Value).Take(pageSize.Value);
} else {
return pizzas;
}
});
app.MapPost("/pizza", (Pizza pizza) => pizzas.Add(pizza));
app.Run();
public record Pizza(int Id, string Name);
Run this app with dotnet run
In your browser, try various things like:
- "http://localhost:{PORT}/pizzas", should give you all pizzas back
- "http://localhost:{PORT}/pizzas?page=1&pageSize=2", should give you the two first pizzas. See how the query parameters are working for you.
- "http://localhost:{PORT}/pizzas/2", should give you the "Al Tonno" pizza back. Here you have the
{id}
matching the 2 and thereby it filters down on the one item that matches.
Learn more
Check out these LEARN modules on learning to use minimal API