We will now proceed to write CRUD operations for an Event API. The aim is to Create, Read, Update, and Delete. Once we have these foundations, it will be much easier to grasp more concepts.
Just after builder.Build(); you should have the following source code:
//create an event
app.MapPost("/event", async (Event newEvent, AppDbContext db) =>
{
db.Events.Add(newEvent);
await db.SaveChangesAsync();
return Results.Created($"/event/{newEvent.Id}", newEvent);
});
//get all events
app.MapGet("/events", async(AppDbContext db) =>
await db.Events.ToListAsync());
//get event by id
app.MapGet("/event/{id}", async (int id, AppDbContext db) =>
await db.Events.FindAsync(id)
is Event @event
? Results.Ok(@event)
: Results.NotFound());
//update an event
app.MapPut("/event/{id}", async (int id, Event inputEvent, AppDbContext db) =>
{
var @event = await db.Events.FindAsync(id);
if (@event is null) return Results.NotFound();
@event.Title = inputEvent.Title;
@event.Description = inputEvent.Description;
@event.Capacity = inputEvent.Capacity;
@event.StartTime = inputEvent.StartTime;
@event.EndTime = inputEvent.EndTime;
await db.SaveChangesAsync();
return Results.NoContent();
});
//delete an event
app.MapDelete("/event/{id}", async (int id, AppDbContext db) =>
{
if (await db.Events.FindAsync(id) is null)
{
return Results.NotFound();
}
else if (await db.Events.FindAsync(id) is Event @event)
{
db.Remove(@event);
await db.SaveChangesAsync();
return Results.NoContent();
}
return Results.NoContent();
});
The last line of code should be app.Run();
Code Breakdown
Let's break down the source code for better understanding:
This route handles HTTP POST requests sent to /event. Its purpose is to create and save a new event in the in-memory database.
Code
Explanation
app.MapPost("/event")
/event is the path where the clients send the event data
(Event newEvent, AppDbContext db)
Two parameters are injected: the event data from the request body i.e., Postman or REST Client (newEvent) and AppDbContext from the service container (db)
db.Events.Add(newEvent)
Adds the new event to the in-memory database's Events Collection
await db.SaveChangesAsync();
Saves changes to the database asynchronously (non-blocking)
//get all events
app.MapGet("/events", async(AppDbContext db) =>
await db.Events.ToListAsync());
What This Does:
This route handles HTTP GET requests sent to /events. It retrieves a list of all events stored in the in-memory database.
Code
Explanation
app.MapGet("/events")
Route to the path /events
AppDbContext db
Injects AppDbContext via dependency injection to interact with the database
await db.Events.ToListAsync()
Asynchronously fetches all event records from the Events table and returns them as a list
Testing with REST Client
Hold Ctrl + Q on your keyboard and search for "Endpoint Explorer"
It will give you all the available endpoints
Right-click on the GET request and generate the request
On the generated endpoint, add a valid endpoint:
GET {{0.EventApi_HostAddress}}/events
3. Get an Event by ID
//get event by id
app.MapGet("/event/{id}", async (int id, AppDbContext db) =>
await db.Events.FindAsync(id)
is Event @event
? Results.Ok(@event)
: Results.NotFound());
What This Does:
This route handles HTTP GET requests sent to /event/ . It looks up a single event using the provided id from the database. If the event exists, it returns the event data; if not, it returns a "Not Found" response.
code
Explanation
app.MapGet("/event/")
route a GET with dynamic id in the url path i.e /event/3
(int id, AppDbContext db)
The route handler takes the id from the URL and usesDependency Injection to get the AppDbContext.
await db.Events.FindAsync(id)
Looks for the event with the matching id in the database. Returns null if not found.
is Event @event ? Results.Ok(@event) : Results.NotFound())
If the event exists, returns a 200 OK response with the event. If it doesn’t, returns 404 Not Found.
Testing with REST Client (Get by ID)
Hold Ctrl + Q on your keyboard and search for "Endpoint Explorer"
It will give you all the available endpoints
Right-click on the GET{id} request and generate the request
This route handles HTTP PUT requests to update an existing event in the database. The client provides an updated version of the event, and if the event exists, its values are modified and saved.
Code
Explanation
MapPut("event/{id}")
Sets up a PUT route for updating an event by ID
(int id, Event inputEvent, AppDbContext db)
The route handler receives the event id from the URL, the updated event data from the request body, and a database context via Dependency Injection
await db.Events.FindAsync(id)
Tries to find the event in the database using the provided id
if (@event is null) return Results.NotFound()
If no event is found, it returns a 404 Not Found response
@event.Title = inputEvent.Title ...and the rest
Updates each field of the found event with the new values provided
await db.SaveChangesAsync()
Commits the changes to the database
return Results.NoContent()
Returns a 204 No Content response to indicate the update was successful, but there's nothing to return in the body
Testing with REST Client (Update)
Hold Ctrl + Q on your keyboard and search for "Endpoint Explorer"
It will give you all the available endpoints
Right-click on the UPDATE request and generate the request
On the generated endpoint, add the following data for testing:
//delete an event
app.MapDelete("/event/{id}", async (int id, AppDbContext db) =>
{
if (await db.Events.FindAsync(id) is null)
{
return Results.NotFound();
}
else if (await db.Events.FindAsync(id) is Event @event)
{
db.Remove(@event);
await db.SaveChangesAsync();
return Results.NoContent();
}
return Results.NoContent();
});
What This Does:
This endpoint handles HTTP DELETE requests to remove an event from the database by its ID. It first checks if the event exists and deletes it if found.
Code
Explanation
app.MapDelete("/event/{id}")
Sets up a DELETE route for removing an event by ID
(int id, AppDbContext db)
The handler receives the event id from the URL and uses the injected database context db
if (await db.Events.FindAsync(id) is null)
Checks if the event exists. If not, returns 404 Not Found
else if (await db.Events.FindAsync(id) is Event @event)
Retrieves the event again and stores it in @event
db.Remove(@event)
Marks the event for deletion
await db.SaveChangesAsync()
Commits the deletion to the database
return Results.NoContent()
Returns a 204 No Content response indicating successful deletion
Testing with REST Client (Delete)
Hold Ctrl + Q on your keyboard and search for "Endpoint Explorer"
It will give you all the available endpoints
Right-click on the DELETE request and generate the request
On the generated endpoint, add a valid id:
DELETE {{0.EventApi_HostAddress}}/event/5
MapGroup API
You will realize that events URL prefix repeats itself. That is what we should fix with MapGroup method which help us to organize such groups to avoid it being repeated.
Replace Program.cs with the following:
using _0.EventApi.Models;
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<AppDbContext>(options => options.UseInMemoryDatabase("EventDb"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
var app = builder.Build();
//MapGroup API
var events = app.MapGroup("/events");
//create an event
events.MapPost("/", async (Event newEvent, AppDbContext db) =>
{
db.Events.Add(newEvent);
await db.SaveChangesAsync();
return Results.Created($"/event/{newEvent.Id}", newEvent);
});
//get all events
events.MapGet("/", async(AppDbContext db) =>
await db.Events.ToListAsync());
//get event by id
events.MapGet("/{id}", async (int id, AppDbContext db) =>
await db.Events.FindAsync(id)
is Event @event
? Results.Ok(@event)
: Results.NotFound());
//update an event
events.MapPut("/{id}", async (int id, Event inputEvent, AppDbContext db) =>
{
var @event = await db.Events.FindAsync(id);
if (@event is null) return Results.NotFound();
@event.Title = inputEvent.Title;
@event.Description = inputEvent.Description;
@event.Capacity = inputEvent.Capacity;
@event.StartTime = inputEvent.StartTime;
@event.EndTime = inputEvent.EndTime;
await db.SaveChangesAsync();
return Results.NoContent();
});
//delete an event
events.MapDelete("/{id}", async (int id, AppDbContext db) =>
{
if (await db.Events.FindAsync(id) is null)
{
return Results.NotFound();
}
else if (await db.Events.FindAsync(id) is Event @event)
{
db.Remove(@event);
await db.SaveChangesAsync();
return Results.NoContent();
}
return Results.NoContent();
});
app.Run();
Breakdown:
var events = app.MapGroup("/events"); - to set up the group using the URL prefix /events.
Changes all the app.Map<HttpVerb> methods to todoItems.Map<HttpVerb>.