Building Single Page Applications on ASP.NET Core 2.2

The recently released ASP.NET Core 2.2 includes many improvements such as updated SPA templates. The templates can be used to build single page apps with Angular 6 + Angular CLI or React 16 + Create React App. These templates come configured with Bootstrap 4.

This post will cover getting started, productivity tips, and steps to implement Open API using NSwag.

Note that this post consolidates content from the previous series, Building Single Page Applications on ASP.NET Core 2.1 with Angular 6.

Let’s get started.

   

Prerequisites

To develop apps using the new project templates, ensure you meet the follow prerequisites.

Check the .NET Core version by running this command:

dotnet --list-sdks

Check the node version by running this command:

node -v

   

Getting Started

Creating a new SPA with ASP.NET Core is easy. There are three project templates available. ASP.NET Core with:

  1. Angular (angular)
  2. React.js (react)
  3. React.js and Redux (reactredux)

Create a new SPA using one of the above templates by executing the following command:

dotnet new [short name]

Choosing either angular, react, or reactredux as the short name. Next, launch the application by running the following command:

dotnet run

Note the application may take a few minutes to load, as on first run it will restore the npm dependencies. 

Once complete, launch the browser and navigate to https://localhost:5001. Take a moment to explore the new app, as it includes an overview of technologies and features along with a couple of sample components.

   

Productivity Tips

Run the front end independently

As per the documentation, the project is configured to start the front end in the background when ASP.NET Core starts in development mode. This feature is designed with productivity in mind. However, when making frequent back end changes productivity can suffer as it takes up to 10 seconds to launch the application after a back end change.

You can launch the front end independently by updating the Configure method within the Startup class.

For the Angular template, update as follows:

// spa.UseAngularCliServer(npmScript: "start");
spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");

For the React templates, update as follows:

// spa.UseReactDevelopmentServer(npmScript: "start");
spa.UseProxyToSpaDevelopmentServer("http://localhost:3000");

Then, to launch the application, open a command line to start the front end:

cd ClientApp
npm start

Next, open a second command line and start the back end:

dotnet run

This will ensure your application will launch quickly when making either front end or back end changes.

   

Launch the back end with dotnet watch

Currently, changes to back end files require restarting the ASP.NET Core server. We can avoid this altogether by running:

dotnet watch run

The watch tool monitors backend files for changes, automatically building and restarting the server when changes are detected. Should the build fail, the tool simply waits for a new change, rebuilds, and continues normally.

By running the front end independently and launching the back end with dotnet watch, changes to the front end and back end will be built and available immediately, leaving you free to write more code!

   

Level Up

In this section, we will take your development to the next level. We will look at automating a number of processes including generating API clients, and reducing the amount of code you need to write.

Reviewing the FetchDataComponent

When building Single Page Applications, front end / back end communication is a major factor. Take a look at the following example:

Figure: The Fetch Data sample included with SPA templates

The diagram above illustrates communication for the sample:

  1. On the front end, using the HttpClient the FetchDataComponent requests weather forecasts from the back end
  2. The back end responds by providing a response of type WeatherForecast[]
  3. The front end receives the response, maps the back end type to a matching type on the front end, and displays the results

While relatively simple, the sample component illustrates numerous issues:

  1. Requires knowledge of API   the developer must be familiar with the APIs capabilities
  2. Hard-coded URLs   the sample component uses hard-coded URLs to access the API
  3. Duplication of back end types   the WeatherForecasttype is created manually on the front end and back end (violates DRY)
  4. Breaking changes are hard to detect   changes to the backend API endpoints and associated types will not be automatically detected by the front end

In this section we will resolve all of these issues by implementing Open API with NSwag.

   

Generate an API specification

Open API, formally known as Swagger API, is an API description format for REST APIs. In this section we will use Open API to supercharge our app by creating interactive documentation and generating a front end client. As an added bonus we will automate this process on build, thereby increasing productivity.

Update the SampleDataController. Within the Controllers sub-directory update the SampleDataController to include the ApiController attribute:

[ApiController]
[Route("api/[controller]")]
public class SampleDataController : Controller

This ensures that SampleDataController will be included in the API specification. Generate the API specification. The API specification describes the available endpoints, operation parameters, authentication methods, contact information, license, terms of use, and other information.

Install and launch NSwagStudio. NSwagStudio is part of the NSwag project, which  provides tools to generate API specifications from existing Web API controllers and client code from these API specifications.

We will use NSwagStudio to generate an API specification by first creating an NSwag configuration. Ensure you are working with a new configuration by clicking File | New.

Define the new configuration as follows:

RuntimeNetCore21. Works fine with .NET Core 2.2.
GeneratorASP.NET Core via API Explorer (new)
Project file pathBrowse to and select the SPA project .csproj file
NoBuildChecked. NSwagStudio will not build the app before generating the specification.
Default Property Name HandlingCamelCase
Output file pathThis is the file path to the new API specification file. Recommend to add it to wwwroot, e.g. C:\Code\AwesomeSpa\wwwroot\docs\api-specification.json.
Output typeOpenAPI 3

Save the NSwag configuration file. Click FileSave, name the file nswag.json and save it to the project directory, e.g. C:\Code\AwesomeSpa\nswag.json.

Generate the API specification. Click Generate Files to generate the specification document within wwwroot.

   

Add and configure Swagger UI

Now that we have a API specification we can utilise Swagger UI, a tool to visualise and interact with the API’s resources without having any implementation logic in place.

Install the NSwag.AspNetCore package. From the project directory, run the following command:

dotnet add package NSwag.AspNetCore

Add and configure Swagger UI.

Open Startup.cs and import the following namespace:

using NSwag.AspNetCore;

Within Startup.Configure, just after app.UseSpaStaticFiles, insert the following:

app.UseSwaggerUi3(settings =>
{
    settings.SwaggerUiRoute = "/docs";
    settings.SwaggerRoute = "/docs/api-specification.json";
});

Launch the project and navigate to https://localhost:5001/docs to view Swagger UI in action.

   

Generate a TypeScript client (Angular only)

In this section we will update the NSwag configuration to generate an Angular TypeScript client.

Open the existing configuration. Under Outputs check TypeScript Client. Select the TypeScript Client tab followed by the Settings child tab, and update as follows:

TypeScript Version2.7
TemplateAngular
RxJs Version6.0
HTTP service classHttpClient
Injection token typeInjectionToken
Output file pathThis is the file path to the new TypeScript client file. Recommend adding it to /ClientApp/src/app, e.g. C:\Code\AwesomeSpa\ClientApp\src\app\app.generated.ts.

Save the NSwag configuration file. Click Generate Files to generate the API specification and the new TypeScript client.

   

Automating with MS Build

So far the generation of the API specification and TypeScript client has been a manual process driven by NSwagStudio. In this section we will automatic this process. When the project builds, the NSwag CLI will run and generate an updated API specification and TypeScript client.

Install the NSwag.MSBuild package. From the project directory, run the following command:

dotnet add package NSwag.MSBuild

Add a new build task. Open the project .csproj and insert the following element before the PublishRunWebpack task:

<Target Name="NSwag" AfterTargets="Build">
  <Copy SourceFiles="@(Reference)" DestinationFolder="$(OutDir)References" />
  <Exec Command="$(NSwagExe_Core21) run /variables:Configuration=$(Configuration)" />
  <RemoveDir Directories="$(OutDir)References" />
</Target>

Build the solution. You will see the following output:

Figure: NSwag MS Build integration

The API specification and TypeScript client are now automatically generated when the project builds. In the next section we will refactor the FetchDataComponent and resolve the issues identified earlier.

   

Refactoring  FetchDataComponent (Angular only)

The generated TypeScript client includes a SampleDataClient that we can use to access the API and fetch weather forecasts. But first we need to register it with dependency injection.

Within /ClientApp/src/app update app.module.ts as follows.

Import the SampleDataClient.

import { SampleDataClient } from './app.generated';

Register the client by updating the providers.

providers: [SampleDataClient],

Finally, update the FetchDataClient as follows:

import { Component } from '@angular/core';
import { SampleDataClient, WeatherForecast } from '../app.generated';

@Component({
  selector: 'app-fetch-data',
  templateUrl: './fetch-data.component.html'
})
export class FetchDataComponent {
  public forecasts: WeatherForecast[];

  constructor(client: SampleDataClient) {
    client.weatherForecasts().subscribe(
      result => this.forecasts = result, 
      error => console.error(error));
  }
}

This is a massive improvement and previous issues have been resolved. In summary:

  1. Requires knowledge of API   Resolved. The developer can now explore the APIs capabilities through Swagger UI or the TypeScript client.
  2. Hard-coded URLs   Resolved. The client manages the URLs, the base URL can be injected.
  3. Duplication of back end types    Resolved.  The WeatherForecast type is created in the back end and generated in the front end, changes to the type will be synchronised.
  4. Breaking changes are hard to detect   Resolved. Breaking changes will be detected by Angular CLI when the new TypeScript client is regenerated.

   

Next Steps

In this post I have provided an overview of Building Single Page Applications on ASP.NET Core 2.2. If you would like to learn more about any of these technologies, take a look at the following resources:

Thanks for reading, please feel free to post any questions or comments below.