Using NSwag to Generate React Client for an ASP.NET Core 3 API

This week we are going to add a React project that will utilize the contacts API we created a few weeks ago. 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. For details on how the associated sample got to the current point in the application 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

I realize that using an ASP.NET Core backed React project for this sample is overkill and a raw React application would have been all that is needed. I chose to use the ASP.NET Core template as a base for all the projects in this series to be consistent. After the initial application creation, you can think of this example as setting up access to a secondary API in addition to the application’s main API if that helps or the generated client on the React side could be used to wrap the API generated by the template.

The sample code before any of the changes in this post can be found here.

Create the React Project

Add a new directory for the React project and then open a terminal set to that directory. The following command can be used to create a new React project. The target framework isn’t required, but I have a preview of .NET Core 3.1 installed and I wanted to make sure this project is targeting .NET Core 3.0.

dotnet new react -f netcoreapp3.0

Next, use the following command to add the new project to the solution file which is in the root of the repo. Your filenames and paths will vary if you are not using the sample code of course.

dotnet sln ..\..\BasicsRefresh.sln add ContactsReact.csproj

Use NSwagStudio to Generate React Client

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

Next, make sure your API is running and get the URL of its OpenAPI/Swagger specification URL. For example, I am using a local instance of my API and the URL I need 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, but I highlighted the options that were important for this sample. First, make sure Module name and Namespace are both empty. I’m sure there is a way to get the client working with a module or namespace, but I didn’t have any luck.   For Template, we just need a Fetch 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 React project directory under ClientApp\src\app\components\contactApi.ts. After all the options are set click Generate Files.

Create UI and Use Generated Client

Once the above is done once you switch back to Visual Studio you should see the following prompt to add the Microsoft.TypeScript.MSBuild NuGet package. The React template doesn’t use TypeScript and NSwag doesn’t have an option to generate a plain JavaScript client so adding this package will allow the build process to take our TypeScript client and convert it to JavaScript. There is an open issue requesting a JavaScript generator.

The sample API is for contact management so the UI we are going to build is to display a contact list. In the ClientApp/src/component directory add a new file named ContactList.js with the following contents. The lines specific to the usage of the NSwag generated client are highlighted.

import React, { Component } from 'react';
import { ContactsClient } from './contactsApi';  

export class ContactList extends Component {
    static displayName = ContactList.name;

    constructor(props) {
        super(props);
        this.state = { contacts: [], loading: true };
    }

    componentDidMount() {
        this.populateContactData();
    }

    static renderContactsTable(contacts) {
        return (
            <table className='table table-striped' aria-labelledby="tabelLabel">
                <thead>
                    <tr>
                        <th>Name</th>
                        <th>Address</th>
                        <th>City</th>
                        <th>State</th>
                        <th>Postal Code</th>
                        <th>Phone</th>
                        <th>Email</th>
                    </tr>
                </thead>
                <tbody>
                    {contacts.map(contact =>
                        <tr key={contact.id}>
                            <td>{contact.name}</td>
                            <td>{contact.address}</td>
                            <td>{contact.city}</td>
                            <td>{contact.state}</td>
                            <td>{contact.postalCode}</td>
                            <td>{contact.phone}</td>
                            <td>{contact.email}</td>
                        </tr>
                    )}
                </tbody>
            </table>
        );
    }

    render() {
        let contents = this.state.loading
            ? <p><em>Loading...</em></p>
            : ContactList.renderContactsTable(this.state.contacts);

        return (
            <div>
                <h1 id="tabelLabel" >Contacts</h1>
                {contents}
            </div>
        );
    }

    async populateContactData() {
        let client = new ContactsClient();
        client.getContacts()
              .then(data => this.setState({ contacts: data, loading: false }));
    }
}

As you can see from the populateContactData code above we are creating a new instance of the ContactsClient and calling its getContacts function and using the data we get back from the API to set the state of the component with the data return from the API.

Now that the contact list is ready it needs a link in the navbar. First, in the App.js file, we need to add the contact list to the router. The following is the full file with the added lines highlighted.

import React, { Component } from 'react';
import { Route } from 'react-router';
import { Layout } from './components/Layout';
import { Home } from './components/Home';
import { FetchData } from './components/FetchData';
import { Counter } from './components/Counter';
import { ContactList } from './components/ContactList';

import './custom.css'

export default class App extends Component {
  static displayName = App.name;

  render () {
    return (
      <Layout>
        <Route exact path='/' component={Home} />
        <Route path='/contacts' component={ContactList} />
        <Route path='/counter' component={Counter} />
        <Route path='/fetch-data' component={FetchData} />
      </Layout>
    );
  }
}

Now to make to add a Contacts link to the navbar open the NavMenu.js file and add the following to the with the other nav items.

<NavItem>
    <NavLink tag={Link} className="text-dark" to="/contacts">Contacts</NavLink>
</NavItem>

Wrapping  Up

I had a bit more trouble getting the NSwag client working this round, but that was more due to my shallow knowledge with React than a problem with NSwag.

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


Also published on Medium.

Leave a Reply

Your email address will not be published. Required fields are marked *

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