Building a Minimal API in C#: A Step-by-Step Guide
Minimal APIs in .NET offer a simplified method for creating APIs with minimal setup and configuration, making them ideal for small—to medium-sized applications or microservices. This article will explain why you opt for a minimal API, describe its basics, address validations, showcase dependency injection, and incorporate CRUD operations. We will also guide you through the project setup process in detail.
Why Minimal API?
Minimal APIs are designed for simplicity and ease of use. They require less boilerplate code compared to traditional MVC or Web API frameworks, making them ideal for scenarios where quick development and low overhead are crucial. Minimal APIs:
It is easier to set up and get running.
Offer better performance due to less overhead.
They are perfect for microservices and lightweight APIs.
Simplify the development process by reducing boilerplate.
Minimal API Fundamentals
Let's create a minimal API in C# from scratch. We'll start by setting up a new project.
Create a New Project:- Open your terminal and run the following command to create a new .NET project.
dotnet new web -n MinimalApiDemo
cd MinimalApiDemo
Configure Your Project:- Edit the `Program.cs` file to define your minimal API. Here's a simple example:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello, Minimal API!");
app.Run();
Run your application using:
dotnet run
Navigate to `http://localhost:5000` to see "Hello, Minimal API!".
Validations in Minimal API:- To add validations, we can use model binding and validation attributes. Create a new file named `Models.cs`:
using System.ComponentModel.DataAnnotations;
public class Person
{
[Required]
public string Name { get; set; }
[Range(0, 120)]
public int Age { get; set; }
}
Update `Program.cs` to include a POST endpoint with validation:
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.ComponentModel.DataAnnotations;
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/", () => "Hello, Minimal API!");
app.MapPost("/person", async (Person person) =>
{
var results = new List<ValidationResult>();
var context = new ValidationContext(person);
if (!Validator.TryValidateObject(person, context, results, true))
{
return Results.BadRequest(results);
}
return Results.Ok(person);
});
app.Run();
public class Person
{
[Required]
public string Name { get; set; }
[Range(0, 120)]
public int Age { get; set; }
}
Dependency Injection:- Minimal APIs fully support dependency injection (DI). Create a service interface and implementation:
public interface IGreetingService
{
string Greet(string name);
}
public class GreetingService : IGreetingService
{
public string Greet(string name) => $"Hello, {name}!";
}
Register and use the service in `Program.cs`:
var builder = WebApplication.CreateBuilder(args);
// Register the service
builder.Services.AddScoped<IGreetingService, GreetingService>();
var app = builder.Build();
app.MapGet("/", () => "Hello, Minimal API!");
app.MapGet("/greet/{name}", (IGreetingService greetingService, string name) =>
{
return greetingService.Greet(name);
});
app.Run();
// Written in same calss to show whole code together
public interface IGreetingService
{
string Greet(string name);
}
public class GreetingService : IGreetingService
{
public string Greet(string name) => $"Hello, {name}!";
}
CRUD Functionality in Minimal API
Having familiarized ourselves with all the fundamentals, we will carry out basic CRUD operations for a straightforward in-memory data store. We will establish an IPerson Repository and use it to instantiate an actual person Repository object, which will then be utilized for the CRUD operation within the API.
public interface IPersonRepository
{
IEnumerable<Person> GetAll();
Person GetById(int id);
void Add(Person person);
void Update(Person person);
void Delete(int id);
}
public class PersonRepository : IPersonRepository
{
private readonly List<Person> _people = new();
public IEnumerable<Person> GetAll() => _people;
public Person GetById(int id) => _people.FirstOrDefault(p => p.Id == id);
public void Add(Person person)
{
person.Id = _people.Count + 1;
_people.Add(person);
}
public void Update(Person person)
{
var index = _people.FindIndex(p => p.Id == person.Id);
if (index != -1)
{
_people[index] = person;
}
}
public void Delete(int id)
{
var person = _people.FirstOrDefault(p => p.Id == id);
if (person != null)
{
_people.Remove(person);
}
}
}
Update the `Person` model in `Models.cs` to include an ID:
public class Person
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Range(0, 120)]
public int Age { get; set; }
}
Register the repository and create CRUD endpoints in `Program.cs`:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddScoped<IPersonRepository, PersonRepository>();
var app = builder.Build();
// Default Get end point
app.MapGet("/", () => "Hello, Minimal API!");
// Get All people end point
app.MapGet("/people", (IPersonRepository repo) =>
{
return repo.GetAll();
});
// Get Person by ID
app.MapGet("/people/{id}", (IPersonRepository repo, int id) =>
{
var person = repo.GetById(id);
return person is not null ? Results.Ok(person) : Results.NotFound();
});
// Add person
app.MapPost("/people", (IPersonRepository repo, Person person) =>
{
repo.Add(person);
return Results.Created($"/people/{person.Id}", person);
});
// Update Person by ID
app.MapPut("/people/{id}", (IPersonRepository repo, int id, Person updatedPerson) =>
{
var person = repo.GetById(id);
if (person is null) return Results.NotFound();
updatedPerson.Id = person.Id;
repo.Update(updatedPerson);
return Results.NoContent();
});
// Delete Person By ID
app.MapDelete("/people/{id}", (IPersonRepository repo, int id) =>
{
repo.Delete(id);
return Results.NoContent();
});
app.Run();
// Added in same file to show the whole code
public class Person
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
[Range(0, 120)]
public int Age { get; set; }
}
public interface IPersonRepository
{
IEnumerable<Person> GetAll();
Person GetById(int id);
void Add(Person person);
void Update(Person person);
void Delete(int id);
}
public class PersonRepository : IPersonRepository
{
private readonly List<Person> _people = new();
public IEnumerable<Person> GetAll() => _people;
public Person GetById(int id) => _people.FirstOrDefault(p => p.Id == id);
public void Add(Person person)
{
person.Id = _people.Count + 1;
_people.Add(person);
}
public void Update(Person person)
{
var index = _people.FindIndex(p => p.Id == person.Id);
if (index != -1)
{
_people[index] = person;
}
}
public void Delete(int id)
{
var person = _people.FirstOrDefault(p => p.Id == id);
if (person != null)
{
_people.Remove(person);
}
}
}
Conclusion
By following these steps, you have successfully developed a minimal API in C# that encompasses validation, dependency injection, and CRUD functionality and showcases the efficiency and simplicity of utilizing minimal APIs. These APIs serve as a robust solution for constructing web services that are both lightweight and high-performing while requiring minimal configuration and reducing unnecessary overhead. Embracing minimal APIs empowers developers to focus on the core functionality of their applications without getting bogged down by complex setup processes, allowing for a more streamlined development experience. With this approach, you can efficiently create scalable and responsive web services that meet the demands of modern applications. Happy coding!
Comments