Build a Backend with ASP.NET Core — Beginner Guide
ASP.NET Core, a powerful and open-source framework from Microsoft, provides a superb foundation for building efficient and scalable backends.
In this blog series, we’ll be constructing a comprehensive Courses API. This API will serve as a central hub for retrieving information about courses and their instructors, laying the groundwork for a dynamic web application.
Welcome to the first blog of this exciting series! Today, we’ll dive into the essentials of ASP.NET Core and build a simple, yet functional, Course API.
Why ASP.NET Core?
Here’s why ASP.NET Core is a compelling choice for crafting your backend systems:
- Cross-platform — Develop and run your backends on Windows, Linux, or macOS.
- Performance — ASP.NET Core consistently ranks among the fastest web frameworks, giving your applications an edge.
- Flexibility — Its modular design allows you to pick and choose the exact components you need, reducing overhead.
- Strong Community and Support — Backed by Microsoft and a large community, you’ll find plenty of resources and help when needed.
- Modern Tooling — Works seamlessly with popular development tools like Visual Studio and Visual Studio Code.
What We’ll Cover
In this blog post, we’ll guide you through the essentials of building a backend with ASP.NET Core. Get ready to dive into…
- Setting up your development environment
- Creating your first ASP.NET Core web API project
- Implementing basic CRUD operations (Create, Read, Update, Delete)
Getting Started
Download .NET Latest version (.NET 8.0 is the latest version when drafting this blog and .NET 9.0 was on preview) from the official site https://dotnet.microsoft.com/en-us/download
Open VS Code and type CTRL+P to access the Quick Dialog and type .NET New Project and select it.
As a beginner, starting from a clean slate is always good. So, choose the ASP.NET Core Web, Empty template.
Select a project location, give your project a suitable name, and set up your development environment. We’ll be building a Courses API for this demo project, so I’ve named the project ‘Courses API’.
After the project setup, the IDE project structure should look something like the above.
Extension to Download
C# Dev Kit
This is your all-in-one toolkit for streamlined .NET development. It includes:
- C# for Visual Studio Code (powered by OmniSharp): The backbone for intelligent code editing, navigation, and debugging.
- Project System for Visual Studio Code: Improved handling of your .NET project files for better organization.
- MSBuild for Visual Studio Code: Allows you to run essential build commands directly inside your editor.
NuGet Package Manager
.NET projects often use many external libraries. This extension is your best friend for finding, installing, and keeping those libraries up-to-date.
Project Structure and Generated Files
launchSettings.json
As per starters, you may need to know a bit about the launchSettings.json file you may find in your “properties” folder.
Think of launchSettings.json as a configuration center for how your application launches during development. Here’s what it helps you control:
- Environment Variables: You can set different environment variables (like “Development,” “Staging,” or “Production”) and your application can behave differently based on those settings.
- Application URLs: Defines the ports and addresses your application will listen on when you start it up for testing.
- IIS Settings: If you’re using IIS Express (a lightweight web server for development), you can configure relevant settings here.
- Browser Launch: You can even tell it which browser to open automatically when running your project.
Program.cs
This file is the heart of your ASP.NET Core application. It’s the starting point where everything begins. Think of it as the main gate to your app. This involves creating a web server and configuring things like how your application handles incoming requests.
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
- The WebApplicationBuilder — The
WebApplication.CreateBuilder(args)
method sets the stage. It creates an object (builder
) that helps you configure and construct all the essential parts of your web application. - The WebApplication — This
builder.Build()
is where the builder takes all your configurations and assembles them into the actualWebApplication
object (app
). Your application is now ready to run!
app.MapGet("/", () => "Hello World!");
- Mapping Routes —
app.MapGet("/", ...)
tells your app, "When someone sends an HTTP GET request to the root path ('/'), respond with the text 'Hello World!'". This is the most basic way to define a route or an endpoint in your API.
app.Run();
- Starting the Engine —
app.Run()
gets your web application up and running! It starts the web server and begins listening for incoming requests. Your application is now live and waiting to respond to those requests.
Running the Application
Navigate to correct project location on the terminal and enter
dotnet run
Use any of your REST Clients and make a GET request to the root endpoint.
Cautious: Double-check your local host port in the launchSettings.json file.
Course Mappings for CRUD Operations
Initial Data setup
As we won’t be exploring database connectivity in this blog, we’ll start the project with a hardcoded course list. However, since C# is a strictly typed language, we’ll first need to define DTOs (Data Transfer Objects) to represent a Course object.
A DTO is a class specifically designed to hold the data we want to transfer between different parts of our application, or even between our application and external systems (like an API).
Start by creating a new folder called Dtos. In the folder, create a new Dto called “GetCoursesDto.cs”. We will be storing the course ID, name, description, no of chapters and instructor ID as fields for now.
namespace CourseAPI.Dtos;
public record class CreateCourseDto(
string Name,
string Description,
int NoOfChapters,
string InstructorId
);
Now we can have a list of courses to perform CRUD Operations. Create a list of dummy “GetCoursesDto” objects as follows in the program.cs file.
List<GetCoursesDto> courses = [
new (
1,
"Node Backend Development",
"This is a demo course",
20,
"1"
),
new (
2,
"React Development",
"This is a Full course",
10,
"2"
),
new (
3,
"Java with OOP Internship Bootcamp",
"This is a Full course",
20,
"2"
)
];
GET All Courses and One Course Mapping
In RESTful APIs, the HTTP verb ‘GET’ is used to retrieve data.
GET All Courses
Route “courses” defines the base URL path users will access for this piece of functionality.
app.MapGet("courses", () => courses);
Lambda Function() => courses
is where you write the logic. Here, it returns thecourses
list which contains all the course data. When the endpoint is reached, it simply returns the entire courses
list.
GET One Course
In the “courses/{id}”, the {id}
part makes this route dynamic. It will match requests like "/courses/1", "/courses/2", etc., where the number represents a specific course ID.
app.MapGet("courses/{id}",(int id)=> {
return courses.Find(course =>course.Id == id);
}).WithName("GetCourse");
- Lambda Function:
(int id) => ...
- Theid
inside the parentheses is automatically extracted from the route based on its position after "courses/".courses.Find(course =>course.Id == id)
- This line searches yourcourses
list for the first course with an ID matching theid
from the request. - WithName(“GetCourse”): This part assigns a name to the route. You’ll see later how this name can be useful for things like generating links within your application.
Post a New Course
When creating a new entry, the Post mapping would be utilized. To typesafe the payload, we can create a new model called CreateCourseDto.
Create a new record file in the Dtos folder as following
namespace CourseAPI.Dtos;
public record class CreateCourseDto(
string Name,
string Description,
int NoOfChapters,
string InstructorId
);
Previously, we used MapGet
to retrieve course information. Now let's explore how we can add new courses to our list using MapPost
. Route “courses” specifies the base path for adding new courses.
app.MapPost("courses",(CreateCourseDto newCourse)=>{
int id = courses.Count + 1;
GetCoursesDto course = new (id,newCourse.Name,newCourse.Description,newCourse.NoOfChapters,newCourse.InstructorId);
courses.Add(course);
return Results.CreatedAtRoute("GetCourse",new {id= id}, course);
});
(CreateCourseDto newCourse)
defines an input parameter representing the data of the new course to be created. We would be utilizing theCreateCourseDto
to model this data.int id = courses.Count + 1;
– A simple way to generate a new unique ID for our course (you'll likely replace this with a database-generated ID in a real application).GetCoursesDto course = new (...)
- Creates a newGetCoursesDto
object representing the course to be added.courses.Add(course);
– Adds the newly created course object to your in-memorycourses
list.return Results.CreatedAtRoute(...)
– This is a successful response! It does three things:
- Provides an HTTP status code of 201 (Created).
- Includes a “Location” header indicating where the newly created course can be found using the ‘GetCourse’ route defined earlier.
- Returns the created ‘course’ object in the response body.
Update an Existing Course
Our application isn’t complete without the ability to modify existing course information. For this, we’ll use MapPut.
In the “courses/{id}” route, the {id}
makes this route dynamic, signalling that we need to provide the ID of the course we wish to update. Depending on the business logic, you can also create a separate Dto for the Update payload. But for the current implementation, the CreateCourseDto
can be utilized.
app.MapPut("courses/{id}",(int id, CreateCourseDto updatedCourse) =>{
GetCoursesDto? currCourse = courses.Find(course=> course.Id == id);
if(currCourse == null){
return Results.NotFound();
}
GetCoursesDto newCourse = new(
id,
updatedCourse.Name,
updatedCourse.Description,
updatedCourse.NoOfChapters,
updatedCourse.InstructorId
);
courses[id-1] = newCourse;
return Results.Ok();
});
(int id, CreateCourseDto updatedCourse)
– Takes the ID from the URL and the updated course data as input parameters.GetCoursesDto? currCourse = ...
– Attempts to find the existing course using its ID. The '?' allowscurrCourse
to be null if no match is found.if(currCourse == null) ...
– Error handling! If the course is not found, it returns a '404 Not Found' response.GetCoursesDto newCourse = new(...)
– Creates a new course object incorporating the changes fromupdatedCourse
.courses[id-1] = newCourse;
– Replace the old course in your list with the newly updated version.return Results.Ok();
– Signals a successful update with a status code of 200 (OK).
Delete a Course
Finally, let’s enable the ability to remove courses from our collection. This is where the HTTP verb ‘DELETE’ comes into play. The “courses/{id}” route includes {id}
to identify the specific course we want to delete.
app.MapDelete("courses/{id}",(int id)=> {
int courseId = courses.FindIndex(course => course.Id == id);
if(courseId == -1){
return Results.NoContent();
}
courses.RemoveAt(id-1);
return Results.NoContent();
});
int courseId = courses.FindIndex(...)
– Finds the index (position) of the course with the matching ID within yourcourses
list.if(courseId == -1) ...
– Another bit of error handling. If the course is not found, it returns a '204 No Content' response, as technically there's nothing to delete.courses.RemoveAt(id-1)
– Removes the course from your list using the index found earlier.return Results.NoContent();
– Sends a '204 No Content' response to signal a successful deletion.
Congratulations. You have built your 1st simple API with ASP.NET Core. So let’s try to understand what’s left to explore.
What to Explore in the Next Blog
While we’ve built a solid foundation for a Courses API, there’s always more to learn in ASP.NET Core development. In the upcoming blog, we’ll dive into some exciting concepts that will level up your API skills.
- Extension methods and route groups — Bring code organization and efficiency to your API endpoints.
- Input validation — Ensure your API receives clean and reliable data.
- Data models, database connectivity, and data querying — Introduce persistent storage through a database and learn to interact with it effectively.
- Entity mapping — Bridge the gap between your code’s objects and database tables.
- Dependency injection and service lifetime — Enhance the flexibility and testability of your application.
- Asynchronous programming — Keep your API responsive and snappy, especially when dealing with time-consuming operations.
Get ready to take your ASP.NET Core backend development skills to new heights. See you in the next post!