ASP.NET Core Minimal APIs let us create web APIs quickly with less code—like a fast way to build a to-do list app. In this guide, we’ll make a Todo API step by step using .NET 8. It’ll let us add, view, update, and delete tasks. We’ll use a database (SQL Server) in Docker and add Swagger to test it easily. Let’s go, one simple step at a time!
Step 1: Get Our Tools Ready
Before we start, we need these on our computer:
- .NET 8 SDK: Lets us build .NET apps. We can get it from dotnet.microsoft.com.
- Docker: Runs SQL Server in a container. We’ll install Docker Desktop from docker.com.
- Text Editor: We’ll use Visual Studio, VS Code, or anything we like.
Docker Setup: We’ll use Docker Compose to run SQL Server. Here’s what we need later in a file called docker-compose.yml
:
services:
sql-server:
image: mcr.microsoft.com/mssql/server:2022-latest
container_name: sql-server
hostname: sql-server
environment:
- ACCEPT_EULA=Y
- MSSQL_SA_PASSWORD=MySecurePassword123!
ports:
- "1433:1433"
restart: unless-stopped
networks:
- sql_network
networks:
sql_network:
driver: bridge
- We’ll save this in our project folder when we’re ready.
Quick Check: Let’s open a terminal (like Command Prompt) and type dotnet --version
. If it shows “8.0.xxx,” we’re set!
Step 2: Project Setup
Let’s create a new project for our Todo API.
-
Open Our Terminal:
- We’ll go to a folder where we want our project (e.g.,
cd Documents
).
- We’ll go to a folder where we want our project (e.g.,
-
Make the Project:
- Run the following command:
dotnet new web -n TodoApp --framework net8.0
This makes a TodoApp
folder with a basic web app.
-
Go Inside the Folder:
- Go to the project directory:
cd TodoApp
-
What We’ve Got:
- Here’s the project layout as a tree structure:
TodoApp/ ├── Program.cs # Our main code file where we’ll build the API ├── TodoApp.csproj # A list of what the project needs (like tools) └── Properties/ └── launchSettings.json # Sets how the app runs (like which port)
- This is what we start with after running the command.
Step 3: Add the Tools We Need
Our API needs extra tools to work with a database and show a testing page.
-
Add Packages:
- We’ll run these in the terminal:
dotnet add package Microsoft.EntityFrameworkCore dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.EntityFrameworkCore.Tools dotnet add package Swashbuckle.AspNetCore
- What they do:
-
EntityFrameworkCore
: Talks to the database. -
SqlServer
: Works with SQL Server. -
Tools
: Helps set up the database. -
Swashbuckle
: Adds Swagger, a testing webpage.
-
-
Check the Project File:
- We’ll open
TodoApp.csproj
. It should look like this:
<Project Sdk="Microsoft.NET.Sdk.Web"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <Nullable>enable</Nullable> <ImplicitUsings>enable</ImplicitUsings> </PropertyGroup> <ItemGroup> <PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.2" /> <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="9.0.2"> <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> <PrivateAssets>all</PrivateAssets> </PackageReference> <PackageReference Include="Swashbuckle.AspNetCore" Version="7.2.0" /> </ItemGroup> </Project>
- We’ll open
- This shows all the tools are added with their versions.
Step 4: Start SQL Server with Docker
We need a database to save our to-dos. Let’s use the Docker setup we saw earlier.
-
Make the Docker File:
- In the
TodoApp
folder, we’ll createdocker-compose.yml
. - We’ll copy this in (from Step 1):
services: sql-server: image: mcr.microsoft.com/mssql/server:2022-latest container_name: sql-server hostname: sql-server environment: - ACCEPT_EULA=Y - MSSQL_SA_PASSWORD=MySecurePassword123! ports: - "1433:1433" restart: unless-stopped networks: - sql_network networks: sql_network: driver: bridge
- In the
-
Start SQL Server:
- Run the following command to start the SQL Server container:
docker-compose up -d
- It runs in the background.
-
Check It’s Running:
- Run the following command:
docker ps
- We should see
sql-server
. If it’s there, we’re good!
Step 5: Tell the App How to Find the Database
We need to give our app the database’s address and password.
-
Make a Settings File:
- We’ll create
appsettings.json
in theTodoApp
folder. - We’ll add this:
{ "ConnectionStrings": { "DefaultConnection": "Server=localhost;Database=TodoDb;User Id=sa;Password=MySecurePassword123!;TrustServerCertificate=True;" }, "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*" }
- We’ll create
- What it does:
-
DefaultConnection
: Says “use SQL Server onlocalhost
, databaseTodoDb
, usersa
, password from Docker.”
-
Step 6: Define a To-Do Item
Let’s decide what a to-do looks like.
-
Make a Models Folder:
- We’ll create a folder called
Models
inTodoApp
.
- We’ll create a folder called
-
Add the Todo File:
- Inside
Models
, we’ll createTodo.cs
and add:
namespace TodoApp.Models; public class Todo { public int Id { get; set; } // A number for each to-do public string Title { get; set; } = string.Empty; // The task, like "Call Mom" public bool IsCompleted { get; set; } = false; // Done or not }
- Inside
- This is a simple way to describe a to-do.
Step 7: Connect to the Database
We need a helper to link our app to the database.
-
Make a Data Folder:
- We’ll create a folder called
Data
inTodoApp
.
- We’ll create a folder called
-
Add the Helper:
- Inside
Data
, we’ll createAppDbContext.cs
and add:
using Microsoft.EntityFrameworkCore; using TodoApp.Models; namespace TodoApp.Data; public class AppDbContext : DbContext { public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { } public DbSet<Todo> Todos { get; set; } = null!; // Our to-do list in the database }
- Inside
- It connects our app to
TodoDb
.
Step 8: Build the API
Now, let’s make the API work with all the pieces.
-
Open
Program.cs
:- We’ll replace everything with:
using Microsoft.EntityFrameworkCore; using TodoApp.Data; using TodoApp.Models; var builder = WebApplication.CreateBuilder(args); // Connect to the database builder.Services.AddDbContext<AppDbContext>(options => options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"))); // Add Swagger for testing builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); var app = builder.Build(); // Show Swagger when testing if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(options => { options.SwaggerEndpoint("/swagger/v1/swagger.json", "Todo API v1"); }); } // Create the database if it’s not there using (var scope = app.Services.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>(); dbContext.Database.EnsureCreated(); } // Add a new to-do app.MapPost("/todos", async (AppDbContext db, Todo todo) => { db.Todos.Add(todo); await db.SaveChangesAsync(); return Results.Created($"/todos/{todo.Id}", todo); }); // Get all to-dos app.MapGet("/todos", async (AppDbContext db) => { return await db.Todos.ToListAsync(); }); // Get one to-do app.MapGet("/todos/{id}", async (AppDbContext db, int id) => { var todo = await db.Todos.FindAsync(id); return todo is not null ? Results.Ok(todo) : Results.NotFound(); }); // Update a to-do app.MapPut("/todos/{id}", async (AppDbContext db, int id, Todo updatedTodo) => { var todo = await db.Todos.FindAsync(id); if (todo is null) return Results.NotFound(); todo.Title = updatedTodo.Title; todo.IsCompleted = updatedTodo.IsCompleted; await db.SaveChangesAsync(); return Results.NoContent(); }); // Delete a to-do app.MapDelete("/todos/{id}", async (AppDbContext db, int id) => { var todo = await db.Todos.FindAsync(id); if (todo is null) return Results.NotFound(); db.Todos.Remove(todo); await db.SaveChangesAsync(); return Results.NoContent(); }); app.Run();
-
What’s Happening:
- Database: Connects to SQL Server.
- Swagger: Adds a testing page.
-
Actions: Sets up five things we can do:
- Add (
POST /todos
). - List all (
GET /todos
). - Get one (
GET /todos/{id}
). - Update (
PUT /todos/{id}
). - Delete (
DELETE /todos/{id}
).
- Add (
Step 9: Check How the App Runs
Our app has a file called launchSettings.json
that decides where it runs (like on port 5240).
-
Look at
launchSettings.json
:- We’ll open
Properties/launchSettings.json
. It looks like this:
{ "$schema": "http://json.schemastore.org/launchsettings.json", "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:52287", "sslPort": 44350 } }, "profiles": { "http": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, "applicationUrl": "http://localhost:5240", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "https": { "commandName": "Project", "dotnetRunMessages": true, "launchBrowser": true, "applicationUrl": "https://localhost:7006;http://localhost:5240", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } } } }
- We’ll open
-
What This Means:
- This file was made when we created the project.
-
Profiles: Three ways to start the app:
-
"http"
: Runs onhttp://localhost:5240
(plain HTTP). -
"https"
: Runs onhttps://localhost:7006
andhttp://localhost:5240
(secure and plain options). -
"IIS Express"
: Uses a local web server (like in Visual Studio).
-
-
Port 5240: For HTTP, it uses
5240
. That’s where we’ll test it! - launchBrowser: Opens our browser when the app starts.
Step 10: Create the Database
The app will make the database when it starts.
-
Easy Way:
- The
EnsureCreated()
line inProgram.cs
createsTodoDb
and aTodos
table the first time we run.
- The
-
Optional Better Way:
-
For a real app, we can use migrations:
- Install the tool:
dotnet tool install --global dotnet-ef
- Make a migration:
dotnet ef migrations add InitialCreate
- Create the database:
dotnet ef database update
- Remove
EnsureCreated()
fromProgram.cs
.
-
-
Check It:
- We’ll use SQL Server Management Studio:
- Connect to
localhost
, usersa
, passwordMySecurePassword123!
. - Look for
TodoDb
.
Step 11: Run and Test the API
Let’s try it out!
-
Start the App:
- We’ll type:
dotnet run --project TodoApp.csproj
- It’ll say “Now listening on: http://localhost:5240”.
-
Open Swagger:
- We’ll go to
http://localhost:5240/swagger
in our browser. - We’ll see a page with all our API actions.
- We’ll go to
-
Play with It:
-
Add a To-Do: We’ll click
POST /todos
, “Try it out”, and add:
{ "title": "Learn .NET 8", "isCompleted": false }
-
Add a To-Do: We’ll click
Click “Execute”. We’ll get a new to-do with an ID.
-
List All: We’ll use
GET /todos
. -
Get One: We’ll try
GET /todos/1
(use our ID). -
Update: We’ll use
PUT /todos/1
with:
{ "title": "Master .NET 8", "isCompleted": true }
Delete: We’ll use
DELETE /todos/1
.
Step 12: What We’ve Got
Here’s what’s in our project:
- Program.cs: Runs the app and defines the API.
- Models/Todo.cs: Describes a to-do.
- Data/AppDbContext.cs: Links to the database.
- appsettings.json: Has the database address.
- Properties/launchSettings.json: Sets the port (5240 for HTTP).
- docker-compose.yml: Runs SQL Server.
Wrap-Up
Awesome job! We’ve made a Todo API with .NET 8 Minimal APIs. It’s simple, uses Docker for the database, and has Swagger to test it. Want more? We can add checks for bad input or login stuff later. For now, let’s have fun with our API—happy coding!
Top comments (0)