Get started with minimal API for .NET 6

Chris Noring - Nov 8 '21 - - Dev Community

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 and namespace are gone as well, so this code:
   using System;
   namespace Application
   {
        class Program
        {
            static void Main(string[] args)
            {
                Console.WriteLine("Hello World!");
            }
        }
   }
Enter fullscreen mode Exit fullscreen mode

is now this code:

   Console.WriteLine("Hello World!");
Enter fullscreen mode Exit fullscreen mode
  • Routes Your routes aren't mapped to controller classes but rather setup with a Map[VERB] function, like you see above with MapGet() 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
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

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);
Enter fullscreen mode Exit fullscreen mode

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" });
    });
Enter fullscreen mode Exit fullscreen mode

Creating the app instance

Here's the next line:

var app = builder.Build();
Enter fullscreen mode Exit fullscreen mode

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!");
Enter fullscreen mode Exit fullscreen mode

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();
Enter fullscreen mode Exit fullscreen mode

 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"));
Enter fullscreen mode Exit fullscreen mode

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();


Enter fullscreen mode Exit fullscreen mode

Where you to run this code, with dotnet run and navigate to /pizza you would get a JSON response:

{
  "pizza" : {
    "id" : 1,
    "name" : "Margherita"
  }
}
Enter fullscreen mode Exit fullscreen mode

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);

Enter fullscreen mode Exit fullscreen mode

Run this app with dotnet run

In your browser, try various things like:

 Learn more

Check out these LEARN modules on learning to use minimal API

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .