Getting started with Minimal APIs in .Net 6
Tags: dotnet, minimalapi, dotnet6, csharp
Published: 2021-11-15 13:24:40

### Intro

This week Microsoft has released .Net 6, which is major mile stone. .Net 6 is an LTS release which means its support for the next 3 years.

Part of this major release Asp.Net Core Minimal API where released. In this video we will take a look at Minimal API discuss what they are how do they work and how we can get started with it.

You can watch the full video on Youtube
{% youtube Ny2q79eD6OI %}

You can get the source code on GitHub using the link below
https://github.com/mohamadlawand087/v52-minimalAPi

So what are we going to cover today:

- What is Minimal API
- Benefits of Minimal API
- Ingredients
- Code time

As always you will find the source code in the description down below. Please like, share and subscribe if you like the video. It will really help the channel

### What is Minimal API

It is a fresh new approach to building APIs without all the complex structure of MVC, it could seems like a prototyping tool for some but for other it could look like a stepping stone into the world of APIs.

Minimal simply means that it contains the essential components needed to build HTTP APIs. Basically with minimal API all you need is a csproj and a program.cs to get started

Minimal API uses the power of C# 9 and .NET 6.0 it combines Top Level statement and Lambda Attributes of C# and abstracting alot of the pipeline and  configuration of MVC

### Benefit of Minimal API

- Reduce complexity
- Reduce ceremony for all developers
- Easy to get started: all you need is the Program.cs and your csproj and 3 lines of code to create your first API
- Its powered by .Net 6 and C# 10 so all of the latest improvements and functionalities are supported out of the box
- API without controllers
- Performance: since the application is so simple alot og bootstrapping that is required to build and compile the application is simplified which means the application runs much faster

### Ingredients

dotnet 6 SDK

First we will create our application 

```bash
dotnet new webapi -minimal -o TodoApi
```

Let us open this in Visual Studio Code and we can examin the file structure, as we can see the folder structure is way smaller then normal APIs, no more startup class or controller folders all we have is a Program.cs

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rgbfygpt0ayefgshlyrz.png)

Let us now Open Program.cs and clear everything from it so we can build a very simple API wi the less then 4 lines of code (Yeah its a crazy world)

The first thing we will need is to have a Web application builder

```csharp
// Initialise a web application from our console application
var app = WebApplication.CreateBuilder(args).Build();

// Map the default route
app.MapGet("/", () => "Hello friends");

// run the application
app.Run();
```

Now let us run this, inside our terminal 

```csharp
dotnet run
```

With these 3 lines of code we now have a fully working api which can be shipped to production in theory.

Now let us expand on this implementaion to create our todo API, the first thing we will need is a data type to hold which we will use for the to do, so let us create one

```csharp
record Item(int Id, string Title, bool Completed);
```

The next step is to create our in memory database

```csharp
class ItemRepository
{
    private readonly Dictionary<int, Item> _items = new Dictionary<int, Item>();

    public ItemRepository()
    {
        var item1 = new Item(1, "Go to the gym", false);
        var item2 = new Item(2, "Buy bread", false);
        var item3 = new Item(3, "Watch TV ", false);
        _items.Add(item1.Id, item1);
        _items.Add(item2.Id, item2);
        _items.Add(item3.Id, item3);
    }

    public IEnumerable<Item> GetAll() => _items.Values;

    public Item GetById(int id) => _items[id];

    public void Add(Item item) {
         _items.Add(item.Id, item);
    }

    public void Update(Item item) => _items[item.Id] = item;

    public void Delete(int id) => _items.Remove(id);
}
```

Once we added the repository we will need to register it with our application, we will modify our first 2 lines to the following

```csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<ItemRepository>();
var app = builder.Build();
```

The reason we did this so we can utilise the builder to do some dependency injection instead of us manually injecting the repositories and handeling the life cycle.

Now that we have finished the setup of our repository and did all of the configuration its time to create the endpoints of our application.

We will start by creating the GetAll items which basically returns all of the items in our todo list

```csharp
app.MapGet("/items", ([FromServices] ItemRepository items) =>
{
    return items.GetAll();
});
```

Now we will add the get by id 

```csharp
app.MapGet("/items/{id}", ([FromServices] ItemRepository items, int id) =>
{
    var result = items.GetById(id);
    return result != null ? Results.Ok(result) :  Results.NotFound();
});
```

Next we need to create an item

```csharp
app.MapPost("/items", ([FromServices] ItemRepository items, Item item) =>
{
    items.Add(item);
    return Results.Created($"/items/{item.Id}",item);
});
```

Then we need to update an item

```csharp
app.MapPut("/items/{id}", ([FromServices] ItemRepository items, int id, Item item) =>
{
    if (items.GetById(id) == null)
    {
        return Results.NotFound();
    }

    items.Update(item);
    return Results.Ok(item);
});
```

Finally let us add the delete endpoint

```csharp
app.MapDelete("/items/{id}", ([FromServices] ItemRepository items, int id) =>
{
    if (items.GetById(id) == null)
    {
        return Results.NotFound();
    }

    items.Delete(id);
    return Results.NoContent();
});
```

Now that we have completed the main endpoints let us see how we can convert our in memory db into an actual SQLite db

Will start by adding some nuget package

```bash
dotnet add package Microsoft.EntityFrameworkCore.Sqlite 
dotnet add package Microsoft.EntityFrameworkCore.Design
```

first we need to add the connection string, inside our app settings let us add the following

```json
"ConnectionStrings": {
    "DefaultConnection": "DataSource=app.db;"
  },
```

Inside our Program.cs let us create a db context which will utilise the item record

```csharp
class ApiDbContext : DbContext
{
    public virtual DbSet<Item> Items {get;set;}

    public ApiDbContext(DbContextOptions<ApiDbContext> options) : base(options){ }
}
```

Then inside our Program.cs let us add the following so we are able to connect to the database

```csharp
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<ApiDbContext>(options =>
                options.UseSqlite(connectionString));
```

Now let us a migration and create the database

```bash
dotnet ef migrations add "initial migration"
dotnet ef database update
```

Now let us update our code to the following and remove the old repository implementation

```csharp
// Replace the recod with a class
class Item {

    public Item(int id, string title, bool completed)
    {
        Id = id;
        Title = title;
        Completed = completed;
    }
    
    public int Id { get; set; }
    public string Title { get; set; }
    public bool Completed { get; set; }
}
```

```csharp
app.MapGet("/items", async (ApiDbContext db) =>
    await db.Items.ToListAsync());

app.MapGet("/items/{id}", async (int id, ApiDbContext db) =>
    await db.Items.FindAsync(id)
        is Item item ? Results.Ok(item) : Results.NotFound());

app.MapPost("/items", async (Item item, ApiDbContext db) =>
    {
        db.Items.Add(item);
        await db.SaveChangesAsync();

        return Results.Created($"/items/{item.Id}", item);
    });

app.MapPut("/items/{id}", async (int id, Item item, ApiDbContext db) =>
    {
        var existItem = await db.Items.FindAsync(id);

        if (existItem is null) return Results.NotFound();

        existItem.Title = item.Title;
        existItem.Completed = item.Completed;

        await db.SaveChangesAsync();

        return Results.NoContent();
    });

app.MapDelete("/items/{id}", async (int id, ApiDbContext db) =>
    {
        if (await db.Items.FindAsync(id) is Item item)
        {
            db.Items.Remove(item);
            await db.SaveChangesAsync();
            return Results.NoContent();
        }

        return Results.NotFound();
    });
```