1. Create an ASP.NET Core Web API Project

Start by creating an ASP.NET Core Web API project targeting the .NET 9 runtime.

2. Add the Required NuGet Packages

Add the following NuGet packages to your project:

<PackageReference Include="NSwag.AspNetCore" Version="13.17.0" />
<PackageReference Include="NSwag.Generation" Version="13.17.0" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <!-- Optional -->

3. Remove Swashbuckle and Add NSwag

Both Swashbuckle and NSwag are used to generate Swagger documentation, but NSwag also provides client code generation features. If you need more details on OpenAPI vs Swagger, check out this link, and for a comparison between Swashbuckle and NSwag, you can watch this video.

a. Remove Swashbuckle: Remove the following code from Program.cs or Startup.cs:

builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
 
app.UseSwagger();
app.UseSwaggerUI(); // If there is any Swashbuckle.AspNetCore installed, uninstall it. Then check https://localhost:XXXX/swagger/index.html

b. Add NSwag: Add the following to configure NSwag:

builder.Services.AddOpenApiDocument(config =>
{
    config.Title = "My API";
    config.Version = "v1";
});
 
app.UseOpenApi();
app.UseSwaggerUi3();

4. Configure CORS Policy

Add a CORS policy to allow requests from Angular or other front-end clients.

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAngularOrigin", policy =>
    {
        policy.WithOrigins("http://localhost:4200")  
              .AllowAnyMethod()
              .AllowAnyHeader()
              .AllowCredentials();
    });
});
 
app.UseCors("AllowAngularOrigin");

5. Built-in Controller Example

Example of a simple built-in controller:

namespace WebApplicationA1.Controllers
{
    [ApiController]
    [Route("[controller]")]
    public class WeatherForecastController : ControllerBase
    {
        private static readonly string[] Summaries = new[] { 
            "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching" 
        };
 
        private readonly ILogger<WeatherForecastController> _logger;
 
        public WeatherForecastController(ILogger<WeatherForecastController> logger)
        {
            _logger = logger;
        }
 
        [HttpGet(Name = "GetWeatherForecast")]
        public IEnumerable<WeatherForecast> Get()
        {
            return Enumerable.Range(1, 5).Select(index => new WeatherForecast
            {
                Date = DateOnly.FromDateTime(DateTime.Now.AddDays(index)),
                TemperatureC = Random.Shared.Next(-20, 55),
                Summary = Summaries[Random.Shared.Next(Summaries.Length)]
            })
            .ToArray();
        }
    }
}

6. Add the Custom Controller

Example of a custom controller:

namespace WebApplicationA1.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class ProductController : ControllerBase
    {
        private static List<string> products = new List<string> { 
            "Product 1", "Product 2", "Product 3", "Product 4", "Product 5", "Product 6" 
        };
 
        [HttpGet]
        public ActionResult<IEnumerable<string>> GetProducts()
        {
            return Ok(products);
        }
 
        [HttpGet("DownloadForecastReport")]
        [ProducesResponseType(typeof(FileContentResult), StatusCodes.Status200OK)]
        public IActionResult DownloadForecastReport()
        {
            var reportData = System.Text.Encoding.UTF8.GetBytes("Sample report data…");
            return File(reportData, "application/octet-stream", "ForecastReport.txt");
        }
    }
}

7. Run the Project

Once the project is running, you can access the Swagger UI and the OpenAPI definition at:

https://localhost:7185/swagger/v1/swagger.json

For TypeScript client: (Run the command in the Package Manager Console)

nswag openapi2tsclient /input:https://localhost:7185/swagger/v1/swagger.json /output:"D:\Users\Shoyeb\Desktop\WebApplicationA1\web-api-client.ts"

For C# client:

nswag openapi2csclient /input:https://localhost:7185/swagger/v1/swagger.json /output:"D:\Users\Shoyeb\Desktop\WebApplicationA1\web-api-client.cs" /namespace:"WebApplicationA1"

8. Auto-generated Code

After running the NSwag command, the client code is auto-generated. Below is an example of the generated TypeScript code:

//----------------------
// <auto-generated>
//     Generated using the NSwag toolchain v14.2.0.0 (NJsonSchema v11.1.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)
// </auto-generated>
//----------------------
 
/* tslint:disable */
/* eslint-disable */
// ReSharper disable InconsistentNaming
 
export class ProductClient {
    private http: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> };
    private baseUrl: string;
    protected jsonParseReviver: ((key: string, value: any) => any) | undefined = undefined;
 
    constructor(baseUrl?: string, http?: { fetch(url: RequestInfo, init?: RequestInit): Promise<Response> }) {
        this.http = http ? http : window as any;
        this.baseUrl = baseUrl ?? "https://localhost:7185";
    }
 
    getProducts(): Promise<string[]> {
        let url_ = this.baseUrl + "/api/Product";
        url_ = url_.replace(/[?&]$/, "");
 
        let options_: RequestInit = {
            method: "GET",
            headers: {
                "Accept": "application/json"
            }
        };
 
        return this.http.fetch(url_, options_).then((_response: Response) => {
            return this.processGetProducts(_response);
        });
    }
 
    protected processGetProducts(response: Response): Promise<string[]> {
        const status = response.status;
        let _headers: any = {}; if (response.headers && response.headers.forEach) { response.headers.forEach((v: any, k: any) => _headers[k] = v); };
        if (status === 200) {
            return response.text().then((_responseText) => {
                let result200: any = null;
                let resultData200 = _responseText === "" ? null : JSON.parse(_responseText, this.jsonParseReviver);
                if (Array.isArray(resultData200)) {
                    result200 = [] as any;
                    for (let item of resultData200)
                        result200!.push(item);
                }
                else {
                    result200 = <any>null;
                }
                return result200;
            });
        } else if (status !== 200 && status !== 204) {
            return response.text().then((_responseText) => {
                return throwException("An unexpected server error occurred.", status, _responseText, _headers);
            });
        }
        return Promise.resolve<string[]>(null as any);
    }
}

9. Create an Angular Project

Create an Angular project and generate a component as follows:

ng new ngLab
ng g c my-component

Add the component to app.component.html:

<app-my-component></app-my-component>

10. Angular Component Example

The TypeScript code for the Angular component:

import { Component, OnInit } from '@angular/core';
import { ProductClient, WeatherForecast, WeatherForecastClient } from '../../../../WebApplicationA1/test.api';
 
@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponentComponent implements OnInit {
  products: string[] = [];
  weatherForecasts: WeatherForecast[] = [];
  errorMessage: string = '';
 
  private productClient: ProductClient;
  private weatherForecastClient: WeatherForecastClient;
 
  constructor() {
    this.productClient = new ProductClient();
    this.weatherForecastClient = new WeatherForecastClient();
  }
 
  ngOnInit(): void {
    this.loadProducts();
    this.loadWeatherForecast();
  }
 
  loadProducts(): void {
    this.productClient.getProducts().then(
      (products) => {
        this.products = products;
      },
      (error) => {
        this.errorMessage = `Error fetching products: ${error.message}`;
      }
    );
  }
 
  loadWeatherForecast(): void {
    this.weatherForecastClient.get().then(
      (forecast) => {
        this.weatherForecasts = forecast;
      },
      (error) => {
        this.errorMessage = `Error fetching weather forecast: ${error.message}`;
      }
    );
  }
 
  downloadForecastReport(): void {
    this.productClient.downloadForecastReport().then(
      (response) => {
        const blob = response.data;
        const link = document.createElement('a');
        link.href = URL.createObjectURL(blob);
        link.download = response.fileName || 'forecast-report.csv';
        link.click();
      },
      (error) => {
        this.errorMessage = `Error downloading forecast report: ${error.message}`;
      }
    );
  }
}

11. Angular HTML Example

The HTML code for displaying products, weather forecast, and a button to download the forecast report:

<div *ngIf="errorMessage" class="error">{{ errorMessage }}</div>
 
<h2>Products</h2>
<ul>
  <li *ngFor="let product of products">{{ product }}</li>
</ul>
 
<h2>Weather Forecast</h2>
<ul>
  <li *ngFor="let forecast of weatherForecasts">
    {{ forecast.date | date }} - {{ forecast.temperatureC }}°C ({{ forecast.temperatureF }}°F) - {{ forecast.summary }}
  </li>
</ul>
 
<button (click)="downloadForecastReport()">Download Forecast Report</button>

12. Additional Notes

You can also use NSwagStudio (the desktop tool. Download) to generate client code. For more details, refer to this blog post.


In the JSON, create a file named config.nswag or nswag.json. Inside this file, include the documentGenerator named specification.json (an exact copy of the OpenAPI YAML), and another file for the generated client, such as api-service.ts (which contains the TypeScript client). Then, in the .csproj file, include a <Target> element so that the client code is automatically generated during the build process. We can also achieve the same thing by using the nswag run nswag.json command in the console manager. Next time, we will try this method.