Building Your First ASP.NET Core RESTful API for Node.js Developers - Introduction (Part 1)

Responses (0)


Over the past decade, many developers started their backend development journey with Node.js. What makes Node.js compelling to developers is the benefit of creating client-side and server-side applications with a single programming language: JavaScript. This convenience, along with the growing interest in frameworks written with modern programming languages like Golang and Rust, means more developers are less likely to branch out to older, more established technologies like ASP.NET. Developed and maintained by Microsoft, ASP.NET (Active Server Pages Network Enabled Technologies) is a framework for creating dynamic web applications and services on the .NET platform. You can write ASP.NET applications with any .NET programming language: C#, F# or Visual Basic. ASP.NET is widely used across many industries, most notably, by large corporations and government agencies.

Despite Microsoft's efforts to adapt ASP.NET to the rapidly evolving web development landscape, such as the release of ASP.NET MVC in 2009 in response to the popularity of MVC frameworks like Django and Ruby on Rails, ASP.NET continued to suffer from several limitations:

  • Dependency on the System.Web.dll Assembly - System.Web.dll enables client-server communication over the HTTP protocol for ASP.NET Web Forms, MVC and Web API. Over time, System.Web.dll accumulated more and more disparate units of functionality and became more integrated with other parts of the .NET Framework. Since System.Web.dll manages so much, making changes to System.Web.dll and releasing them on pace with what's trending in the web development landscape would be impossible.

  • Tight Coupling with IIS (Internet Information Services) - ASP.NET applications could only run on Windows-only IIS web servers. This prevented ASP.NET applications from being built and ran on non-Windows platforms like Linux.

To migrate away from ASP.NET's monolithic design, Microsoft re-implemented ASP.NET as a modular, cross-platform compatible, open-source framework named ASP.NET Core. Released in 2016, ASP.NET Core comes with built-in support for dependency injection and provides...

  • Application Frameworks (e.g., ASP.NET Core MVC)

  • Utility Frameworks (e.g., Identity Framework and Entity Framework)

  • Middleware (e.g., Rate Limiting, Logging and Response Compression)

  • Web Servers (e.g., Kestrel and IIS)

  • Project Templates (e.g., ASP.NET Core Web API, Blazor WebAssembly App, Console Application and Razor Page)

  • Abstractions, Libraries, Packages and APIs (e.g., ASP.NET Core SignalR, Caching, HTTP, Localization and OpenAPI)

With these features, you can build and run lightweight web applications on Windows, Linux and macOS via the .NET Core runtime. In fact, developers can host their web applications not just on IIS, but also, Nginx, Docker, Apache and much more. This all makes ASP.NET Core applications suitable for containerization and optimized for cloud-based environments. For any missing functionality, you can fetch them as packages from NuGet. As the package manager for .NET, NuGet is equivalent to npm for Node.js.

At its core, an ASP.NET Core application is a self-contained console application that self-hosts a web server (by default, the cross-platform web server Kestrel), which processes incoming requests and passes them directly to the application. Once it finishes handling a request, the application passes the response to the web server, which sends the response directly to the client (or reverse proxy). Keeping the web server independent of the application this way makes testing and debugging much simpler, especially when compared to previous versions of ASP.NET where IIS directly executes the application's methods.

So why might Node.js developers consider learning ASP.NET Core? In the latest round of TechEmpower benchmark, ASP.NET Core significantly outperforms Node.js, sending back almost nine times more plaintext responses per second.

Below, I'm going to show you how to build your first ASP.NET Core RESTful API with C#, a strongly-typed, object-oriented language. Throughout this tutorial, I will relate concepts and patterns to those that you may have already encountered in an Express.js RESTful API.

Installation and Setup#

To get started, verify that you have the latest LTS version of the .NET Core SDK, v6, installed on your machine.

The .NET Core SDK (Software Development Kit) consists of everything you need to create and run .NET applications:

  • .NET Core CLI

  • .NET Core Runtime, Libraries and Templates

  • dotnet Driver

If your machine does not have the .NET Core SDK installed, then download the latest LTS version of the .NET Core SDK for your specific platform and follow the installation directions.

Once installed, create a new directory named weather-api. Then, within this directory, create a new solution file:

A solution file lists and tracks all of the projects that belong to a .NET Core application. For example, the application may include an ASP.NET Core Web API project, several class libraries (for directly interfacing with databases via the Entity Framework) and an ASP.NET Core with React.js project. With a solution file, the dotnet CLI knows which projects to restore NuGet packages for (dotnet restore), build (dotnet build) and test (dotnet test) in your application. In this case, you will find a weather-api.sln file within the root of the project directory.

Let's create a new ASP.NET Core Web API project:

The dotnet new command scaffolds a new project or file based on a specified template, such as sln for a solution file and webapi for an ASP.NET Core Web API. The -n option tells the dotnet new command the name of the outputted project/file. In this case, you will find the ASP.NET Core Web API project located within an API directory.

Let's add this project to the solution file:

You can verify that the project has been added to the solution file by running the following command, which lists all of the projects added to the solution file:

If you open the solution file, then you will find the "API" project listed with a project type GUID (FAE04EC0-301F-11D3-BF4B-00C04F79EFBC for C#), a reference to the project's .csproj file and a unique project GUID.

Let's restore the project's NuGet packages. For Node.js developers, this is similar to running npm install/yarn install on a freshly cloned Git repository to reinstall dependencies.

If you are building this project on macOS, then you can find the NuGet packages in the ~/.nuget/packages directory. These packages relate to the package referenced in the API/API.csproj: Swashbuckle.AspNetCore. Swashbuckle.AspNetCore sets up Swagger for ASP.NET Core APIs.

You can check the project's obj/project.nuget.cache file for absolute paths to the project's NuGet packages.

A Look Into the Default ASP.NET Core RESTful API#

Let's take a look at the three C# files in the API directory:

  • Program.cs

  • WeatherForecast.cs

  • Controllers/WeatherForecastController.cs

Much like the index.js file of a simple Express.js RESTful API, the Program.cs file also bootstraps and starts up a RESTful API, but for ASP.NET Core. It follows a minimal hosting model that consolidates the Startup.cs and Program.cs files from previous ASP.NET versions into a single Program.cs file. Plus, the Program.cs file now makes use of top-level statements and implicit using directives to eliminate extra boilerplate code like the class with a Main method and using directives respectively.

As you can see in the Program.cs file, setting up and running a RESTful API with ASP.NET Core requires significantly less code than previous ASP.NET versions.


Program.cs begins with instantiating a new WebApplicationBuilder, a builder for web applications and services. WebApplicationBuilder follows the builder pattern, which breaks down the construction of a complex object into multiple, distinct steps. This means that we delay the creation of the builder object (var app = builder.Build()) until we finish configuring it.

Upon instantiation, the builder object comes with preconfigured defaults for several properties:

Alongside these preconfigured defaults, we explicitly register additional services to the built-in DI (dependency injection) container with WebApplicationBuilder.Services. This DI container simplifies dependency injection in ASP.NET Core (i.e., automatically resolves dependencies and manages their lifetimes) and is responsible for making all registered services available to the entire application. Here, the following methods get called on builder.Services:

  • AddControllers() - Registers the required services for API controllers.

  • AddEndpointsApiExplorer() - Registers the required services for creating the metadata of API endpoints that's commonly consumed by tools like OpenAPI-based generators.

  • AddSwaggerGen() - Registers the required services for the Swagger generator.

After registering these services, call the builder object's Build() method to build the WebApplication (host) with these configurations. By default, the WebApplication uses Kestrel as the web server.

Then, we check if the application is running within a development environment, and if so, then add Swagger middleware (UseSwagger() and UseSwaggerUI()) to the application's middleware pipeline. Notice how these Use{Feature} extension methods that add middleware are prefixed with Use, which is similar to how Express.js calls app.use() to mount middleware functions.

Calling the UseSwaggerUI() method automatically enables the static file middleware. Express.js also provides a built-in middleware function for serving static assets (app.use(express.static("<path_to_static_files>"))).

The remaining middleware gets applied to all requests regardless of environment:

After all of this middleware gets added to the middleware pipeline, we call the MapControllers() method to automatically create an endpoint for each of the application's controller actions and add them to the IEndpointRouteBuilder. This method saves us the trouble of having to explicitly define the routes ourselves.

Lastly, we call the Run() method to run the application.

So to start up the ASP.NET Core RESTful API, run the dotnet run command, which runs the project in the current directory, within the API directory.

Note: If our application consisted of multiple projects, then you can specify which project you want to run by passing a --project option to dotnet run (e.g., dotnet run --project API to just run the API project) without having to change the current directory.

When you run this command, you may come across the following error message:

If you do, then follow the directions in the error message. Run the dotnet dev-certs https --clean command to remove all existing ASP.NET Core development certificates, and run the dotnet dev-certs https to create a new untrusted developer certificate. To trust this certificate, run the command dotnet dev-certs https --trust. Then, re-run the dotnet run command and the error message should no longer pop up. Alternatively, you can remove https://localhost:7101; from applicationUrl in the API/Properties/launchSettings.json file, which stores profiles that tell ASP.NET Core how to run a specific project.

Within a browser, you can visit the Swagger documentation at http://localhost:5077/swagger.

Here, you will find that the RESTful API comes with only a single endpoint: GET /WeatherForecast.

If you expand the endpoint's accordion item, then a summary of the endpoint will appear:

This summary provides an example response (status code, value, etc.) for the endpoint.

If you test this endpoint by visiting http://localhost:5077/WeatherForecast in the browser, or sending a GET request to http://localhost:5077/WeatherForecast via a REST client like Postman or a CLI utility like cURL, then you will get a response that contains four weather forecasts.

To see how the RESTful API handles requests to the GET /WeatherForecast endpoint, open the Controllers/WeatherForecastController.cs file.


If you have developed an Express.js RESTful API, then you should be familiar with the concept of controllers. After all, route callback functions act as controllers.

To understand how this file works, let's first take a look at the [ApiController] attribute. This attribute tells ASP.NET Core that the controller class will opt-in to using opinionated, commonly-used API functionality like multipart/form-data request inference and automatic HTTP 400 responses.

A route attribute ([Route("[controller]")]) is placed on the controller and coerces all controller actions to use attribute routing. The [controller] token in the route attribute expands to the controller's name, so the controller's base URL path is /{controller_name}, or in this case, /WeatherForecast. This means the URL path of /WeatherForecast can match the WeatherForecast.Get() action method. Since this action method is marked with the HttpGet attribute, only GET requests to /WeatherForecast will run this action method.

When declaring it, a controller class in ASP.NET Core RESTful APIs should derive from the ControllerBase class, which provides the properties and methods needed for processing any HTTP request.

This controller only contains one action method, WeatherForecast.Get(). This action method returns four weather forecasts. Each weather forecast is created with the WeatherForecast model that's defined in the API/WeatherForecast.cs file:


This model represents the shape of a weather forecast's data. In the WeatherForecast.Get() action method, we pass several values to the model:

  • A date that's offsetted from the current date based on the current weather forecast's index.

  • A random temperature (in °C) that's within the range of -20 (inclusive) and 55 (exclusive).

  • A random summary from the Summaries array (private static readonly makes this field only visible to this class).

And the model automatically populates each property accordingly.

Next Steps#

Proceed to the second part of this tutorial series to see how to add your own endpoints to this RESTful API.