Recent blog posts
Pete Eamsuwan • 8/18/2023
Set up client code generation, in minutes
Integrate client service/interface code generation to your ASP.NET Core build chain in minutes.
It is not an uncommon for developers working on a Single Page Application (SPA) to sometimes forget to update the front end service and/or models to match the models returned from the back-end, or vice versa. I for one, have fallen victims to this due to my forgetfulness more than once, resulting in errors that are not caught until deployment (hopefully, to your dev environment).
In this article, I will show you how you can quickly integrate client code generation to your backend build configuration. With a properly configured CICD pipeline, the mismatched model could then be picked up by linting or build tools and fail out before deployment.
1. Replace Swashbuckle.AspnetCore with NSwag.AspnetCore and NSwag.MSBuild
Swashbuckle and NSwag are two popular libraries for generating OpenApi documentation, we will be replacing the default library that comes with the WebApi template, with
NSwag.AspnetCore
and NSwag.MSBuild
. You will also need to update the service registration and call the following functions in your Program.cs
builder.Services.AddSwaggerDocument();
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseOpenApi();
app.UseSwaggerUi3();
}
With these changes, running your solution should still bring up the Swagger UI.
2. Configure nswag.json
In your root directory, add a new file called nswag.json. A documentation on how to configure nswag to generation your client code can be found here. A working example can also be found at my Event Sourcing project here.
Take note of the following settings:
needs to be a relative path to your assembly fileassemblyPaths
- Without
set to true, NSwag will not generation a client (by default, only the interface/model)generateClientClasses
- By default, NSwag will use
. If you prefer Axios, set it as your template setting as shown belowFetch
, set the relative path to where your client file will reside.output
"runtime": "Net60",
"defaultVariables": null,
"documentGenerator": {
"webApiToOpenApi": {
...
"assemblyPaths": ["bin/Debug/net6.0/ApiClientGeneration.dll"],
...
}
},
"codeGenerators": {
...
"template": "Axios",
"generateClientClasses": true,
"output": "./ClientApp/src/api/api.ts",
...
}
The configuration can also be created using NSwagStudio as described by this documentation.
3. Add NSwag build target to your csproj file
The
NSwag.MSBuild
package allows us to integrate NSwag code generation to your MSBuild configuration, so that each time your Web Api application is built, the client code is automatically generated. To achieve this, add the following Build target to your csproj file.
<Project Sdk="Microsoft.NET.Sdk.Web">
...
<Target Name="NSwag" BeforeTargets="AfterBuild">
<Exec ConsoleToMSBuild="true" ContinueOnError="true" Command="$(NSwagExe_Net60) run nswag.json">
<Output TaskParameter="ExitCode" PropertyName="NSwagExitCode" />
<Output TaskParameter="ConsoleOutput" PropertyName="NSwagOutput" />
</Exec>
<Message Text="$(NSwagOutput)" Condition="'$(NSwagExitCode)' == '0'" Importance="low" />
<Error Text="$(NSwagOutput)" Condition="'$(NSwagExitCode)' != '0'" />
</Target>
</Project>
That's it, building your solution will now automatically generate the client code for your Api.
//----------------------
// <auto-generated>
// Generated using the NSwag toolchain v13.20.0.0 (NJsonSchema v10.9.0.0 (Newtonsoft.Json v13.0.0.0)) (http://NSwag.org)
// </auto-generated>
//----------------------
/* tslint:disable */
/* eslint-disable */
// ReSharper disable InconsistentNaming
import axios, { AxiosError, AxiosInstance, AxiosRequestConfig, AxiosResponse, CancelToken } from 'axios';
constructor(baseUrl?: string, instance?: AxiosInstance) {
this.instance = instance ? instance : axios.create();
this.baseUrl = baseUrl !== undefined && baseUrl !== null ? baseUrl : "";
}
get( cancelToken?: CancelToken | undefined): Promise<WeatherForecast[]> {
let url_ = this.baseUrl + "/WeatherForecast";
url_ = url_.replace(/[?&]$/, "");
let options_: AxiosRequestConfig = {
method: "GET",
url: url_,
headers: {
"Accept": "application/json"
},
cancelToken
};
// Omitted because this is an autogenerated code
4. (Optional) Add the autogenerated client code to your .gitignore file
The code generation is best executed on the CICD pipeline and then compiled or transpiled with the rest of your client code. This is to ensure that the auto generated code remains auto generated and not tampered or edited manually. Editing auto generated code manually will lead to mistakes as the changes will be blown away the next time it is regenerated.
Concluding remarks
Client code generation is an incredibly powerful tool that can help you maintain consistency between client and server model. I have used this extensively across both commercial and personal projects, and it has proven to be extremely useful in appropriate scenarios. I hope you find this guide useful.