The release of .net 6 came with a with of minimal api's which enable the development of small and functional single file Web API's.
What is a minimal api
Minimal API is an application model for building lightweight Web APIs. Enabling the creation of a microservice or quickly start prototyping without boilerplate code and structure concerns. Essentially Minimal APIs will help developers to build ASP.NET Core apps without ceremony.
Over the past few years, other programming languages and software development frameworks such JS, nodeJS, Go, python etc have been gaining popularity over the more traditional tools such as C# and .net due to the fact that frameworks obfuscate a lot of the boiler plate work required. I have previously posted about how to develop Simple lightweight node.js web server.
The .net framework project templates and such never really had this lightweight touch to developing applications. For the most part this was always primarily driven by the typical environments .net developers found themselves developing typical large monolithic enterprise applications
Over the years things have moved on containerised microservice deployed to Kubernetes are all the vogue and functionality of applications is broken up in 100s or smaller applications.
The advent of .net 6 and the optimisations within the framework itself has enabled new project templates, which include the minimal api
Build a minimal API
We will build a very simple Quotation Service, which will be the basis of several more tutorials going forward. The main responsibility of the service is to provide a get endpoint which can be called to retrieve a randomised quatation per call.
The functionality of the service will be very much a Proof Of Concept, which may evolve as its use expands, but at this stage all we want to deliver is JSON payload with two properties:
- Quote
- Citation
{ "quote" : "minimal api's are cool", "citation" "Gary Woodfine" }
Using the CLI
We will make use of the CLI to generate the initial stub of the service for us. Lets create an initial folder for project, which we'll eventually follow some pretty standard OSS conventions as we'll eventually be using Github to host the project.
mkdir quotations cd quotations
Once we have created our folder and changed into it we can use the Dotnet CLI to create our project using the command below. We will make use of the webapi template but we will provide the switch -minimal
to use the minimal template , we will output the project to src folder using -o src
and we will give the project a name of quotes using -n quotes
dotnet new webapi -minimal -o src -n quotes
This will generate the project for us in the src folder as follows
We could run this project as is, because if we open the Program.cs
and view the code, the .net template has generated a very crude fictitious weather service for us.
var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); var summaries = new[] { "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" }; app.MapGet("/weatherforecast", () => { var forecast = Enumerable.Range(1, 5).Select(index => new WeatherForecast ( DateTime.Now.AddDays(index), Random.Shared.Next(-20, 55), summaries[Random.Shared.Next(summaries.Length)] )) .ToArray(); return forecast; }) .WithName("GetWeatherForecast"); app.Run(); record WeatherForecast(DateTime Date, int TemperatureC, string? Summary) { public int TemperatureF => 32 + (int)(TemperatureC / 0.5556); }
We could run this project using the command below and then browse to https://localhost:7023/weatherforecast
in our browser
dotnet run --project quotes.csproj
We'll stop the service now because we will change it to suit our needs now. Bearing in mind that the entire purpose of minimal API's is to enable you to iterate quickly and to keep boiler plate code out of your way to enable you to focus on delivering business functionality of your service quickly.
With that in mind, our objective at this point is to quickly develop a randomised quotation service, that will provide the consumer of the service a random quote when they make a call. We will try to implement the absolute Minimum amount of code deliver this functionality. Which is borrowing concepts from Eric Ries book, The Lean Startup, and his concept of the Minimum Viable Product (MVP).
The Lean Start Up
How Today's Entrepreneurs Use Continuous Innovation to Create Radically Successful Businesses
The Lean Startup is a new approach being adopted across the globe, changing the way companies are built and new products are launched.
We'll edit the Program.cs
as below. We will attempt keep all the logic for service down to 1 file at the most.
var builder = WebApplication.CreateBuilder(args); // Add services to the container. // Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); var quotations = new List<Quotation> { new("Minimal Api's are cool", "Gary Woodfine"), new("Independent money limits wars", "Dominic Frisby"), new("Our system is broken, our laws don't work, our regulators are weak, our governments don't understand what's happening, and our technology is usurping our democracy", "Christopher Wylie"), new("1% of the population controls 42% of the wealth", "David Graeber"), new("DRY more accurately means that we want to avoid duplicating our system behaviour and knowledge", "Sam Newman"), new("Dapr Requires state stores to support eventual consistency by default", "Haishi Bai & Yaron Schneider") }; app.MapGet("/quote", () => { return quotations.OrderBy(_ => new Random().Next()).Take(1); }) .WithName("quote"); app.Run(); record Quotation(string Quote, string Citation);
Once we make this change, we can now run our project again using the command below and browse to https://localhost:7023/quote
in our browser
dotnet run --project quotes.csproj
Our service is running as expected and further more it is delivering the functionality as defined. We have delivered the minimum amount of code to deliver this functionality.
That being said the template has also provided us with some beneficial development functionality we haven't explored. If we open the project in our favourite IDE, my personal one is Rider: Fast & powerful cross-platform .NET IDE and Run the project
The application will run and it will open a browser session and you will automatically redirected to the Swagger page providing documentation of how to interact with the API
The configuration for Swagger documentation was carried out the top of our Program.cs
and was supplied by the template and as you can see it is automatically configured to only be used during development.
builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Configure the HTTP request pipeline. if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); }
Top-Level Programs
One of the areas that have been improved and optimised when it comes to minimal Api's is the introduction of Top-Level programs
feature of C# 9.0.
Top-Level Programs helps to eliminate boilerplate code by enabling you write any programming statement you want directly after the using
statements and before any type of namespace declarations and you can only do this in one file.
The example below helps to illustrate the old boiler plate code required in .net 5
using System; class Program { static void Main() { Console.WriteLine("Hello World!"); } }
Essentially Top-Level programs
help to eliminate the ceremony of implementing the boiler plate code . This will help newcomers to .net by enabling developers to get going without worrying about learning about namespaces, arrays, arguments etc.
With top-level programs, you can work with normal functions, use async
 and await
, access command-line arguments, use local functions, and more.
If we wanted to we could drastically simply the implementation of our service as follows to reduce the swagger implementation then things will be clearer as to what it requires to get a simple service up and running
var app = WebApplication.CreateBuilder(args).Build(); app.UseHttpsRedirection(); var quotations = new List<Quotation> { new("Minimal Api's are cool", "Gary Woodfine"), new("Independent money limits wars", "Dominic Frisby"), new("Our system is broken, our laws don't work, our regulators are weak, our governments don't understand what's happening, and our technology is usurping our democracy", "Christopher Wylie"), new("1% of the population controls 42% of the wealth", "David Graeber"), new("DRY more accurately means that we want to avoid duplicating our system behaviour and knowledge", "Sam Newman"), new("Dapr Requires state stores to support eventual consistency by default", "Haishi Bai & Yaron Schneider") }; app.MapGet("/quote", () => { return quotations.OrderBy(_ => new Random().Next()).Take(1); }) .WithName("quote"); app.Run(); record Quotation(string Quote, string Citation);
Routing Api
Another big improvement of .net 6 is the routing APi which enables developers to route to any type of method - These methods can use controller-like parameter binding, JSON formatting, and action result execution.
In previous version of .net writing arbitrary response to the browser would need something similar to below which is not entirely intuitive and could be confusing.
endpoints.MapGet("/", async context => { await context.Response.WriteAsync("Hello World!"); });
Using c# 10 features this can drastically simplified to the below, almost giving C# Javascript type abilities, which may be a good or bad feature depending on your view.
app.MapGet("/", (() => "Hello World!"));
In the sample code we have taken advantage of this simplification and implemented a get endpoint /quote
and return a random JSON quote property from our list of predefined quotes.
app.MapGet("/quote", () => { return quotations.OrderBy(_ => new Random().Next()).Take(1); }) .WithName("quote");
Summary
We've had a quick lightning tour of the Minimal Api templates and even implemented a functional service using the template. We learned that the minimal API templates implement new C# language features which enable the functionality to develop the minimal Api
- What is this Directory.Packages.props file all about? - January 25, 2024
- How to add Tailwind CSS to Blazor website - November 20, 2023
- How to deploy a Blazor site to Netlify - November 17, 2023