Using NSwag to Generate an Aurelia Client for an ASP.NET Core 3.1 API

This week we are going to add an Aurelia project that will utilize the contacts API we created a few weeks ago using a client-generated by NSwag. This post is part of the revamp of the ASP.NET Core Basics repo that was kicked off when .NET Core 3.0 was released which is now targeting .NET Core 3.1. For details on how the associated samples got to their current point check out the following posts.

Swagger/OpenAPI with NSwag and ASP.NET Core 3
ASP.NET Core 3: Add Entity Framework Core to Existing Project
New Razor Pages Project Backed with an API
Using NSwag to Generate Angular Client for an ASP.NET Core 3 API
Using NSwag to Generate React Client for an ASP.NET Core 3 API
Using NSwag to Generate Blazor Server Client for an ASP.NET Core 3.1 API
Using NSwag to Generate a Vue Client for an ASP.NET Core 3.1 API

The sample code before any changes from this post can be found here.


 

Create the Aurelia Project

As with Vue there is no .NET CLI template from Microsoft that has Aurelia support so to crate the Aurelia project we will be using the Aurelia CLI. Before getting started ensure you have npm installed.

Install the Aurelia CLI using the following command from a command prompt.

npm install -g aurelia-cli

Next, use the following command to start the project creation process using the Aurelia CLI. Keep in mind that the CLI creates a directory with the project name.

au new

The above will result in a walkthrough of the project creation process. First is the name of the project, contacts-aurelia in this case. Next is the setup of the project and here we will be using the Default TypeScript App.

Finally, select how you would like to manage dependencies. The sample project is using npm, but Yarn is also an option. If you do go with Yarn some of the following steps will need the npm commands translated to Yarn commands.

After the project creation process is complete use the following command to change to the new directory created for the project.

cd contacts-aurelia

Now the project needs a few more dependencies installed. We are going to install a couple of UI related items, Bootstrap and Font Awesome, as well as the Aurelia Fetch Client which we will need to hit our API.

npm install bootstrap
npm install font-awesome
npm install aurelia-fetch-client

The application that the Aurelia CLI outputs is very basics so the Aurelia docs for creating a to-do application and creating a contact manager were used to build the basics of the sample application. I will be coving the contact related bits of the UI, but the application stops short of implementing the save functionality at this point.

Use NSwagStudio to Generate an API Client

NSwag provides multiple options for client generation including a CLI, code, or a Windows application. This post is going to use the Windows application which is called NSwagStudio. NSwagStudio can be downloaded and installed from here.

Next, make sure your API is running and get the URL of its OpenAPI/Swagger specification URL. For example, using a local instance of the sample solution’s Contacts API the URL is https://localhost:5001/swagger/v1/swagger.json. If you are using the Swagger UI you can find a link to your swagger.json under the API title.

Now that we have the OpenAPI/Swager specification URL for the API switch over to NSwagStudio. The application will open with a new document ready to go. There are a few options we will need to set. First, select the OpenAPI/Swagger Specification tab and enter your API’s specification URL in the Specification URL box.

In the Outputs section check the TypeScript Client checkbox and then select the TypeScript Client tab. There are a lot of options to play with, the highlighted options are the ones that are important for this sample. For Template, we just need an Aurelia based client. The final option that needs to be set is the Output file path and this is the location you want the generated file to be. I output to the Aurelia project directory under /src/contactApi.ts. After all the options are set click Generate Files.

Create UI and Use Generated Client

Again the UI bit mostly comes from the docs, but I’m going to show the bits for the contact list here and the rest of the UI you can look at the sample code. All of the following will be taking place in the src directory of the Aurelia project.

First, add a file named contact-list.html which will hold the template for the UI of the contact list with the following contents. This is a mix of HTML and Aurelia’s syntax. We aren’t really going into the Aurelia specific bits, but even if you are new to Aurelia this should be readable.

<template>
  <div class="contact-list">
    <ul class="list-group">
      <li repeat.for="contact of contacts" class="list-group-item ${contact.id === $parent.selectedId ? 'active' : ''}">
        <a route-href="route: contacts; params.bind: {id:contact.id}" click.delegate="$parent.select(contact)">
          <h4>${contact.firstName} ${contact.lastName}</h4>
          <p>${contact.email}</p>
        </a>
      </li>
    </ul>
  </div>
</template>

Next, add a contact-list.ts file which is what the template from above will be bound to. The lines specific to the usage of the NSwag generated client are highlighted.

import {ContactsClient, Contact} from './contactsApi';
import {inject} from 'aurelia-framework';

@inject(ContactsClient)
export class ContactList {
  contacts: Contact[];
  api: ContactsClient;
  selectedId: any;
  
  constructor(api: ContactsClient) {
    this.api = api;
    this.contacts = [];
  }

  created() {
    this.api.getContacts().then(contacts => this.contacts = contacts);
  }

  select(contact) {
    this.selectedId = contact.id;
    return true;
  }
}

As you can see from the above Aurelia is injecting an instance of the ContactsClient via the class’s construction and then that client is used in the created function to call the API client’s getContacts function and using the resulting data from the API to replace the contacts field with the results of the API call.

The application is displaying the contact list in app.html via the contact-list element. The import and usage of the contact list component are highlighted in the following chunk of code.

<template>
  <require from="./styles.css"></require>
  <require from="./contact-list"></require>

  <nav class="navbar navbar-light bg-light fixed-top" role="navigation">
    <a class="navbar-brand" href="#">
      <i class="fa fa-user"></i>
      <span>Contacts</span>
    </a>
  </nav>

  <div class="container">
    <div class="row">
      <contact-list class="col-md-4"></contact-list>
      <router-view class="col-md-8"></router-view>
    </div>
  </div>
</template>

At this point, I tried out the application and it wasn’t pulling back any data. After doing some digging in the network tab of my browser’s dev tool I noticed that the API call was missing the base part of the URL. This hasn’t come up before for the other times I have used the NSwag generated client and if you look at the constructor of the client it defaults the base URL to the endpoint that was used to generate the client, see the following code.

constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
    this.http = http ? http : <any>window;
    this.baseUrl = baseUrl ? baseUrl : "https://localhost:5001";
}

It turns out that the Aurelia dependency injection system calls the constructor with an empty string instead of null. One option would have been to change the above constructor to handle an empty string, but that would mean any time the client got regenerated I would have to remember to follow it up with the constructor modification which would be too easy to screw up. After some digging, I found out that Aurelia provides a way to control how an instance of a class is created. Open main.ts and make the following highlighted changes. I’m injecting the URL, but using null instead would also work and the base URL from the ContactClient would get used.

import { HttpClient } from 'aurelia-fetch-client';
import {Aurelia} from 'aurelia-framework'
import * as environment from '../config/environment.json';
import {PLATFORM} from 'aurelia-pal';
import 'bootstrap/dist/css/bootstrap.css';
import 'font-awesome/css/font-awesome.css';
import { ContactsClient } from 'contactsApi';

export function configure(aurelia: Aurelia) {
  aurelia.use
    .standardConfiguration()
    .feature(PLATFORM.moduleName('resources/index'))
    .instance(ContactsClient, 
              new ContactsClient("https://localhost:5001",
                                 aurelia.container.get(HttpClient)));

  aurelia.use.developmentLogging(environment.debug ? 'debug' : 'warn');

  if (environment.testing) {
    aurelia.use.plugin(PLATFORM.moduleName('aurelia-testing'));
  }

  aurelia.start().then(() => aurelia.setRoot(PLATFORM.moduleName('app')));
}

After all the change from a command prompt set to the root of the Aurelia project, you can use the following command to run the application. If you drop the open it will run the application without opening a browser.

au run --open

Wrapping Up

As always NSwag makes it very easy to create a client to interact with an API. Hopefully, this was useful even if my Aurelia code might not be idiomatic.

The sample projects after all the changes in this post can be found here.


Also published on Medium.

Leave a Comment

Your email address will not be published.

This site uses Akismet to reduce spam. Learn how your comment data is processed.