How to Be More Productive When Creating CRUD APIs in .NET
Learn simple, modern ways to build CRUD APIs faster in .NET 10. Scaffolding, minimal APIs, EF Core, DTO mapping, and reusable patterns explained for beginners.
A tiffin shop where every order follows the same steps
Picture a small tiffin shop in your neighbourhood. Every day it takes orders, cooks the food, packs the dabba, and hands it over. The owner does not invent a brand new system for each customer. He has one smooth routine. Take the order. Cook. Pack. Deliver. Because the steps are always the same, he gets faster and faster. Soon he can serve a hundred customers without breaking a sweat.
Building CRUD APIs is just like running that tiffin shop. CRUD means Create, Read, Update, Delete. These four steps repeat for almost every kind of data: users, orders, products, students, anything. If you set up one clean routine and reuse it, you become very fast. If you reinvent everything each time, you get tired and make mistakes.
This post shows you simple, modern ways to make that routine smooth in .NET 10. We will look at scaffolding, minimal APIs, EF Core, DTO mapping, and a few reusable patterns. By the end, you will be able to add a full set of CRUD endpoints in minutes, not hours.
What CRUD really means
Let us make the four words concrete. Each one maps to an HTTP method, which is just the "verb" a browser or app uses to talk to your API.
| CRUD action | HTTP method | What it does | Example route |
|---|---|---|---|
| Create | POST | Adds a new record | POST /products |
| Read | GET | Fetches one or many records | GET /products/5 |
| Update | PUT or PATCH | Changes an existing record | PUT /products/5 |
| Delete | DELETE | Removes a record | DELETE /products/5 |
That is the whole game. Once you can do these four cleanly for one thing, like a product, you can do them for anything. Here is the flow of a single request through your API.
Step 1: Start fast with scaffolding
The quickest way to begin is to let the tools write the boring parts for you. This is called scaffolding. Instead of typing every controller, every EF Core setup, and every connection string by hand, the tool generates a ready base. You then change the parts that matter.
You can scaffold from Visual Studio 2026, from Visual Studio 2022 version 17.13 or newer, or from VS Code with the C# Dev Kit. The generated code follows standard ASP.NET Core style, so your project stays neat and consistent.
A common starting point is the empty web template, then adding EF Core:
dotnet new web -n ShopApi
cd ShopApi
dotnet add package Microsoft.EntityFrameworkCore.Sqlite
dotnet add package Microsoft.EntityFrameworkCore.DesignScaffolding saves you from repeating the same setup again and again. It is exactly like the tiffin shop owner buying ready dabbas instead of making each box by hand. Your energy goes to the food, not the packaging.
Step 2: Choose minimal APIs for speed of writing
In ASP.NET Core you can build endpoints in two main styles: controllers and minimal APIs. Both sit on the same engine, so they run at nearly the same speed. The real difference is how much code you type.
Minimal APIs let you write a route and its logic in just a few lines. For plain CRUD, that means less boilerplate and faster work. Here is a short comparison.
| Topic | Minimal APIs | Controllers |
|---|---|---|
| Lines of boilerplate | Very few | More setup per file |
| Best for | New, focused CRUD services | Big teams with many existing controllers |
| File organisation | You decide | Folder and class convention |
| Learning curve | Gentle for beginners | A bit more to learn |
| Performance | Excellent | Excellent |
For a fresh CRUD service built by a small team, minimal APIs usually win on speed of writing. Here is a full set of CRUD endpoints for products, all in one readable block.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ShopContext>(o => o.UseSqlite("Data Source=shop.db"));
var app = builder.Build();
var products = app.MapGroup("/products");
// Read all
products.MapGet("/", async (ShopContext db) =>
await db.Products.AsNoTracking().ToListAsync());
// Read one
products.MapGet("/{id:int}", async (int id, ShopContext db) =>
await db.Products.FindAsync(id) is Product p
? Results.Ok(p)
: Results.NotFound());
// Create
products.MapPost("/", async (Product input, ShopContext db) =>
{
db.Products.Add(input);
await db.SaveChangesAsync();
return Results.Created($"/products/{input.Id}", input);
});
// Update
products.MapPut("/{id:int}", async (int id, Product input, ShopContext db) =>
{
var p = await db.Products.FindAsync(id);
if (p is null) return Results.NotFound();
p.Name = input.Name;
p.Price = input.Price;
await db.SaveChangesAsync();
return Results.NoContent();
});
// Delete
products.MapDelete("/{id:int}", async (int id, ShopContext db) =>
{
var p = await db.Products.FindAsync(id);
if (p is null) return Results.NotFound();
db.Products.Remove(p);
await db.SaveChangesAsync();
return Results.NoContent();
});
app.Run();Notice the MapGroup("/products") line. That one line sets the shared route prefix for every endpoint below it. You write /products once, not five times. Small reuses like this add up to big time savings.
The five CRUD endpoints
Steps
List
GET / returns many
Get one
GET /{id} returns one
Create
POST / adds a record
Update
PUT /{id} changes it
Delete
DELETE /{id} removes it
Step 3: Let EF Core handle the database
Entity Framework Core, or EF Core, is the part of .NET that talks to your database for you. You describe your data as a plain C# class, and EF Core handles the SQL. This is a huge productivity boost because you almost never write raw SQL for simple CRUD.
You need two small pieces: an entity class and a DbContext.
public class Product
{
public int Id { get; set; }
public string Name { get; set; } = "";
public decimal Price { get; set; }
}
public class ShopContext : DbContext
{
public ShopContext(DbContextOptions<ShopContext> options) : base(options) { }
public DbSet<Product> Products => Set<Product>();
}The DbSet<Product> line is what gives you db.Products in the endpoints above. EF Core then creates and runs the SQL when you call methods like ToListAsync or SaveChangesAsync.
A quick win: AsNoTracking for reads
When you only want to read data and show it, you do not need EF Core to watch those objects for changes. Adding AsNoTracking() tells EF Core to skip its change tracker. This can cut memory use by about half and make large reads run a few times faster. You saw it in the "Read all" endpoint above. It is a free speed boost, so use it on every read-only query.
Migrations keep your database in step
When you change your classes, you change your tables with a migration. EF Core writes the change steps for you.
dotnet ef migrations add InitialCreate
dotnet ef database updateRun these from your build or deployment pipeline so every environment stays the same. This saves you from hand-editing tables, which is slow and risky.
Step 4: Return DTOs, not raw entities
A DTO (Data Transfer Object) is a small class shaped exactly for what you send out or take in. It is tempting to return your Product entity straight from the API, but that has problems. It can leak fields you did not mean to share. It also ties your public JSON to your database, so a tiny schema change can break the apps that call you.
Think of a DTO like the menu card at the tiffin shop. The customer sees a clean menu, not the messy kitchen. The kitchen can change, but the menu stays steady.
public record ProductResponse(int Id, string Name, decimal Price);
public record CreateProductRequest(string Name, decimal Price);The catch is that copying values from an entity to a DTO by hand is boring and easy to get wrong. This is where a mapper helps.
Use a source generator for mapping
For years, many people used AutoMapper for this. But AutoMapper and MediatR both moved to commercial paid licenses in 2025. So plenty of teams now reach for free tools instead. A great free choice is Riok.Mapperly, a source generator. It writes the mapping code at build time. There is no slow reflection at runtime, and you can even read the generated code if you want.
[Mapper]
public partial class ProductMapper
{
public partial ProductResponse ToResponse(Product product);
public partial Product ToEntity(CreateProductRequest request);
}You write the empty partial methods, and Mapperly fills in the bodies for you. Now your endpoints stay tidy, your secrets stay safe, and you barely type any mapping code.
The DTO boundary
Steps
Client JSON
What the app sends
Request DTO
Validated input shape
Entity
Mapped by Mapperly
Database
Saved by EF Core
Step 5: Reuse one pattern everywhere
The biggest productivity secret is not a fancy library. It is consistency. Once your first set of CRUD endpoints works, copy that same shape for the next thing. Same naming, same return types, same validation style. When every endpoint looks the same, you stop thinking about plumbing and focus on the actual feature.
A helpful habit is TypedResults. Instead of Results.Ok, use TypedResults.Ok. It returns a typed result, which makes your handlers easier to test and gives better OpenAPI docs for free.
products.MapGet("/{id:int}",
async Task<Results<Ok<ProductResponse>, NotFound>> (int id, ShopContext db, ProductMapper map) =>
{
var p = await db.Products.AsNoTracking().FirstOrDefaultAsync(x => x.Id == id);
return p is null
? TypedResults.NotFound()
: TypedResults.Ok(map.ToResponse(p));
});This single handler shows the whole productive style in one place: a route group, AsNoTracking for the read, a DTO out, Mapperly for mapping, and TypedResults for clean, testable responses.
When to consider FastEndpoints
If your project grows past 50 endpoints and a bigger team wants strict structure, look at FastEndpoints. It is a free, open-source library that wraps minimal APIs in a tidy class-per-endpoint style called REPR (Request, Endpoint, Response). It gives you built-in validation, versioning, and a command bus, while staying about as fast as minimal APIs. For small CRUD services, plain minimal APIs are simpler. For large ones, FastEndpoints can keep things organised.
Putting it all together
Here is the full productive routine in order. Each step removes hand-typed work and lets you move faster, just like the tiffin shop owner who polished his routine until he could serve a crowd with ease.
- Scaffold the project so the boring setup is done for you.
- Use minimal APIs and a route group so you write the prefix once.
- Let EF Core talk to the database, and add
AsNoTrackingon reads. - Return DTOs, and map with a free source generator like Mapperly.
- Reuse the exact same shape for every new entity.
Do this, and adding a new set of CRUD endpoints becomes a five-minute job. You spend your time on the interesting parts of your app, not on plumbing you have written a hundred times before.
References and further reading
- Tutorial: Create a Minimal API with ASP.NET Core (Microsoft Learn)
- Tutorial: Create a controller-based web API with ASP.NET Core (Microsoft Learn)
- Riok.Mapperly source generator on GitHub
- ASP.NET Core 10 Web API CRUD with EF Core (codewithmukesh)
- FastEndpoints official documentation
Quick recap
- CRUD is Create, Read, Update, Delete, and it maps to POST, GET, PUT/PATCH, and DELETE.
- Scaffolding writes the boring setup so you start fast.
- Minimal APIs with a
MapGrouproute prefix cut boilerplate for new CRUD services. - EF Core handles the database, and
AsNoTracking()makes reads lighter and faster. - Migrations keep your tables in step with your classes, run from your pipeline.
- DTOs protect your data and decouple your JSON from your database.
- Riok.Mapperly maps entities to DTOs for free, with no reflection. (AutoMapper and MediatR are now paid.)
- Consistency is the real superpower: reuse one clean pattern for every entity.
Related Posts
Getting Started with FastEndpoints for Building Web APIs in .NET
A friendly beginner guide to FastEndpoints in .NET. Learn the REPR pattern, build your first endpoint, add validation, and see how it compares to controllers.
Automatically Register Minimal APIs in ASP.NET Core
Learn to auto-register Minimal API endpoints in ASP.NET Core using the IEndpoint pattern, assembly scanning, and source generators. With diagrams and code.
Best Practices for Building REST APIs in ASP.NET Core
A friendly, beginner guide to REST API best practices in ASP.NET Core with naming, status codes, validation, ProblemDetails, paging, versioning, security, and code.
Building Fast Serverless APIs With Minimal APIs on AWS Lambda
Learn to run ASP.NET Core Minimal APIs on AWS Lambda for fast, cheap serverless APIs. Covers setup, cold starts, Native AOT, and .NET 10 with diagrams and code.
How to Structure Minimal APIs in ASP.NET Core (.NET 10)
Learn how to structure Minimal APIs in ASP.NET Core with route groups, endpoint files, DTOs, TypedResults, and filters. Beginner-friendly with diagrams.
Top 15 Mistakes Developers Make When Creating Web APIs
A warm, beginner-friendly tour of the 15 most common Web API mistakes in ASP.NET Core, with simple fixes, diagrams, tables, and clear C# examples.