Writing client code involves a lot of boilerplate code and becomes tedious whenever you need to update your API and then mirror the required changes in your client code. By generating your client code through swagger and NSwag on build you no longer need to maintain the changes in the client code yourself and it encourages writing more descriptive API endpoints.
In this video, I give a quick demo; however, if you prefer written format, I cover the same material below in slightly greater depth.
Once you have Swagger generating a descriptive Open API JSON document (which is what the Swagger UI pulls from) it is time to use the dotnet tool for swagger to generate this file for our solution files after building our API project. This can be managed by first adding the dotnet swagger tool to our project. If you don’t have a tool manifest in your project already, you can create one using
dotnet new tool-manifest
Then you install the swagger tool locally to this manifest with
dotnet tool install swashbuckle.aspnetcore.cli
Then add the following code to your csproj file to invoke the tool after building the project.
This will result in a swagger.json file being generated after build. This file can be included in your solution and importantly targeted by NSwag and the connected services part of Visual Studio.
Creating an HTTP Client
Now that you have your swagger.json it is time to generate your API client from it using NSwag. In Visual Studio, right click on connected services for your client project and select “Manage Connected Services”
Now under Service References, hit the plus button and then choose OpenAPI.
In this menu, we will be selecting our swagger.json file from our API project though it is worth noting that if the API you want to generate a client for is not one you managed and is a deployed Azure resource, you can just provide the URL to their hosted OpenAPI document and generate client code that way – though in my experience it does not automatically stay up to date like ours will.
Complete the rest of the dialogue, giving your generated code a namespace, class name, and choosing your preferred language. Once you finish you will have your class. At this point, updates to the swagger.json will get picked up when you rebuild your client project, however dotnet doesn’t know that your client project has this dependency on your API project when it builds. You can test this out by removing the swagger document and then building your solution. Both projects try to build but your client project fails because no swagger.json exists. To be clear about this dependency without including your API’s dlls in your client releases, we add the following project reference in your client csproj file.
Now, even if the swagger.json file exists, building the solution will now result in no errors as the API project will build first then the client project will build. This means that anytime you make a big change in your API project, building your solution can provide immediate insight on the downstream impact to your client code.
Extending Your Client
If the client code you’ve generated needs to serialize some tricky custom classes or perhaps do some custom FHIR serialization, don’t panic. You can add your own serializers and modify the serializer settings by leveraging the UpdateJsonSerializerSettings method provided in the generated code for just this purpose.
You can see in the generated code that this method is called during instantiation of the class but that the method itself is just a stub on the partial class.
This means you can provide your own definition for the method in a partial class and overwrite the default JSON settings like so:
You can leverage similar extensibility options with the PrepareRequest and ProcessResponse methods or choose to overwrite methods entirely wherever you deem more manual control is required while still preserving a lot of work being done for you in creating a robust API client.
Now you can write microservices without a lot of the overhead of having to write and maintain client code for each new service all without losing control over the final client methods and regenerating the code is as easy hitting the hotkey for solution build.
You can learn more about the project I do this in, Hackathon Team Builder, here.