Eduardo(エドワード) Lavaque (ラバケ)

Generating a Swagger file with ASP.Net Core and generating API code for Angular

If you ever read The Pragmatic Programmer, you'll be familiar with the concept of Code Generation. It has a section dedicated to it.

In order to avoid writing a bunch of front end code over and over, just to reflect the models and endpoints the back end provides, you can generate it all automatically.

This tutorial will be divided in two sections, the back end section and the front end section.

This tutorial will use C# ASP.Net Core for the back end framework, and Angular for the front end.

Back End (ASP.Net Core)

When you generate a new web application project with Rider it actually already includes Swagger for you in the project, using SwashBuckle. But it doesn't generate any files that you can use outside the server's code.

The aim is to generate a swagger.json file, which we will use later for the front end code. AND, we want to generate it automatically, without having to run extra commands.

To achieve this we will generate the swagger.json file at build time.

I'll be targeting framework net6.0, lang version 10. Reason for this is that it's the solution I found to assembly version mismatches.

The commands you need to run are:

# cd MySolution
dotnet new tool-manifest
dotnet tool install SwashBuckle.AspNetCore.Cli

What this will achieve is make the SwashBuckle CLI tools from the context of the solution.

You will want to make a directory for your Swagger-generated files.

mkdir -p swagger/v1

Next, open up your .csproj file and make sure the following lines are present:

<Target Name="OpenAPI" AfterTargets="Build">
    <Exec Command="dotnet swagger tofile --output ./swagger/v1/swagger.yaml --yaml $(OutputPath)$(AssemblyName).dll v1" WorkingDirectory="$(ProjectDir)" />
    <Exec Command="dotnet swagger tofile --output ./swagger/v1/swagger.json $(OutputPath)$(AssemblyName).dll v1" WorkingDirectory="$(ProjectDir)" />
</Target>

What this will achieve is that after your solution builds it will generate a couple Swagger files, swagger.json and swagger.yaml. In reality you only need the JSON version for the purposes of this tutorial.

While you're at it, make sure your Swashbuckle.AspNetCore version is 6.3.0.

And that should be it. If it's working, when you build your project you should find out that you have a swagger.json and swagger.yaml in your swagger/v1/ folder.

Front end (Angular)

We're going to use a project named ng-openapi-gen to generate the front end models and services, based on the swagger.json file. And we'll use chokidar-cli to run ng-openapi-gen automatically whenever the swagger.json file changes.

So first, we configure ng-openapi-gen via the ng-openapi-gen.json file, we put that in our front end project's root dir:

{
  "$schema": "node_modules/ng-openapi-gen/ng-openapi-gen-schema.json",
  "input": "../server/MySolution/MySolution.Web/swagger/v1/swagger.json",
  "output": "src/app/api",
  "ignoreUnusedModels": false
}

And now for the tooling side of things:

yarn add -D ng-openapi-gen chokidar-cli

And to your npm scripts, you need to add:

{
  "swagger": "ng-openapi-gen",
  "swagger:watch": "chokidar '../server/MySolution/MySolution.Web/swagger/v1/swagger.json' -c 'npm run swagger'"
}

Now you can automatically generate front end code, and stop writing the same code over and over.

ng-openapi-gen resulting folders

Misc.

Setting up Angular

Make sure to follow the instructions on ng-openapi-gen to setup Angular properly to use the generated code for the various cases it supports.

Git

My .gitignore includes these lines for the back end:

MySolution.Web/swagger/

The build process fails if the folders don't exist though. So I suggest you also run the following commands, in order to make sure the folder isn't lost:

touch MySolution.Web/swagger/v1/.keepme
git add -f MySolution.Web/swagger/v1/.keepme

And for the front end:

src/app/api/

In conclusion

With the above the back end's code is used as the source for the swagger.json file, and the front end automatically generates code you can use to access those endpoints, and with TypeScript types to go along with it, so you're all typed up.