- Lab
-
Libraries: If you want this lab, consider one of these libraries.
- Core Tech
Guided: Extending a Minimal API with CRUD Operations and Validation
Build a production-minded ASP.NET Core 10 minimal API by adding full CRUD endpoints, enforcing consistent typed responses, returning Problem Details-style errors, and implementing reusable validation with endpoint filters. You’ll finish by verifying correct behavior with an HTTP client workflow across create, read, update, and delete operations.
Lab Info
Table of Contents
-
Challenge
Step 1: Understand the baseline minimal API and the learning sequence
In this lab you'll expand a minimal API from a small starting point into a consistent CRUD API.
The starter files live in
CourseCatalog.Api:Program.cs— application bootstrap and (currently empty) endpoint mappingsCourses/Models.cs—Course,CourseRequest, andCourseResponserecordsCourses/InMemoryCourseRepository.cs— a thread-safe in-memory store you'll use throughICourseRepositoryValidation/CourseRequestValidator.cs— a placeholder you'll fill in during Step 3Validation/ValidateCourseRequestFilter.cs— anIEndpointFilterplaceholder for Step 3
A companion test project at
CourseCatalog.Api.Testswill verify each task. You can run a single check at any time from theworkspacedirectory with./runTest.sh TestStep<N>Task<M>, or run them all withdotnet test CourseCatalog.Api.Tests.Why the order matters:
- Route groups and typed results define a consistent HTTP surface area early.
- Validation is easiest to apply once you know the shapes of requests and responses.
- Endpoint filters are most valuable after multiple endpoints exist (so you can remove duplication).
- Testing last gives you confidence the entire contract works together, and the HTTP client workflow mirrors real debugging.
Get the project running once before you start: From
CourseCatalog.Api, rundotnet run. The app should start and redirect/to Swagger UI. Stop it withCtrl+Cand you're ready to begin Step 2.info> If you get stuck on a task, you can find solution files with the completed code for each task in the
solutionfolder of your filetree.This lab experience was developed by the Pluralsight team using an internally developed AI tool. All sections were verified by human experts for accuracy prior to publications. However, content may still contain errors or inaccuracies, and we recommend independent verification. To report a problem or provide feedback, click here. Feedback may be used to improve accuracy in accordance with our Privacy Policy.
-
Challenge
Step 2: Add read endpoints and organize routing with route groups
Minimal APIs are great for small services, but they can become hard to maintain if endpoints are scattered. Route groups let you:
- Keep related endpoints together
- Apply shared filters and conventions
- Create a consistent URL hierarchy
Typed results (the
TypedResults.*helpers andResults<TOk, TError>return type) help ensure your handlers return clear, consistent responses and give better OpenAPI metadata than returning arbitrary objects.A quick note about route patterns inside a group: when the group prefix is
/api/courses, mapping a child pattern of""(empty string) yields the route/api/courses, while mapping"/"yields/api/courses/(with a trailing slash). Use""for collection roots in this lab so requests likeGET /api/coursesmatch cleanly.All work in this step happens in
CourseCatalog.Api/Program.cs. -
Challenge
Step 3: Implement validation with parameter binding and endpoint filters
Minimal APIs support binding route parameters and request bodies directly in handler parameters. Validation can be done inline, but endpoint filters (
IEndpointFilter) let you:- Centralize request validation in one place
- Keep handlers focused on business logic
- Standardize error responses (especially validation problems via
TypedResults.ValidationProblem(...))
A Problem Details-style validation response (RFC 9457) gives clients a predictable structure to display or log field-level issues.
In this step you'll implement the validator class, the endpoint filter, and add the first write endpoint (
POST /api/courses) so the filter has something concrete to guard. You'll then refactor by lifting the filter from a single endpoint up to the route group itself—setting up Step 4 for free.Files you'll touch:
CourseCatalog.Api/Validation/CourseRequestValidator.csCourseCatalog.Api/Validation/ValidateCourseRequestFilter.csCourseCatalog.Api/Program.cs
-
Challenge
Step 4: Finish the write surface with consistent typed results
CRUD endpoints should communicate clearly through status codes:
POSTcreate: typically201 Createdwith aLocationheaderPUTupdate: often204 No Content(or200 OKwith the updated representation)DELETE: commonly204 No Contenton success
For failures, Problem Details-style errors (
TypedResults.Problem(...)) keep response formats consistent and client-friendly.In this step you'll upgrade the basic POST you wired up in Step 3 to return a proper
201 Createdwith aLocationheader, then addPUTandDELETEto round out the CRUD surface. The validation filter you applied at the route-group level in Task 3.3 will automatically protect every new write endpoint—no extra wiring needed.All work in this step happens in
workspace/CourseCatalog.Api/Program.cs. -
Challenge
Step 5: Verify behavior using an HTTP client workflow and integration tests
Automated tests ensure you don't regress behavior. A simple HTTP client workflow file is also valuable because:
- It documents common scenarios
- It's easy to run during debugging
- It mirrors how consumers call the API
In this step you'll add both: a human-readable request sequence in
workspace/CourseCatalog.Api/CourseCatalog.httpand an automated integration test inworkspace/CourseCatalog.Api.Tests/Step5Tests.csthat validates the full CRUD lifecycle through the in-memoryWebApplicationFactory<Program>. -
Challenge
Step 6: Wrap up and recap
Congratulations—you've turned a near-empty minimal API into a small but production-minded Course Catalog service.
Along the way you:
- Used
app.MapGroup(...)to organize related endpoints under/api/coursesand apply shared conventions like tags and filters in one place. - Returned consistent, OpenAPI-friendly responses with
TypedResults(Ok,CreatedAtRoute,NoContent,Problem,ValidationProblem) and theResults<T1, T2, ...>union type. - Encapsulated request validation in
CourseRequestValidatorand reused it through anIEndpointFilter(ValidateCourseRequestFilter) attached at the route-group level, so every write endpoint validates input the same way. - Implemented the full CRUD surface—
GET,POST,PUT,DELETE—with thoughtful HTTP semantics (201 Created+Location,204 No Content,404 Problem Details). - Verified your work two ways: a
CourseCatalog.httpworkflow file you can hand to a teammate or run yourself, and an integration test built onWebApplicationFactory<Program>that exercises the entire lifecycle.
A few directions to take this further on your own:
- Swap the
InMemoryCourseRepositoryfor an EF Core-backed implementation behind the sameICourseRepositoryinterface—the endpoints shouldn't have to change. - Add authentication and authorization, then move those concerns onto the
coursesroute group via.RequireAuthorization(...)so they apply consistently. - Introduce paging and filtering on
GET /api/coursesand surface the parameters through OpenAPI. - Add a global exception handler with
app.UseExceptionHandler(...)that renders Problem Details for unhandled errors.
Nice work—you've now got a solid template you can carry into real ASP.NET Core minimal API services.
- Used
About the author
Real skill practice before real-world application
Hands-on Labs are real environments created by industry experts to help you learn. These environments help you gain knowledge and experience, practice without compromising your system, test without risk, destroy without fear, and let you learn from your mistakes. Hands-on Labs: practice your skills before delivering in the real world.
Learn by doing
Engage hands-on with the tools and technologies you’re learning. You pick the skill, we provide the credentials and environment.
Follow your guide
All labs have detailed instructions and objectives, guiding you through the learning process and ensuring you understand every step.
Turn time into mastery
On average, you retain 75% more of your learning if you take time to practice. Hands-on labs set you up for success to make those skills stick.