Swagger is nowadays an essential tool for producing API documentation. It allows describing the routes, parameters, and return of each element of an API.
Swagger is an implementation of OpenAPI, which is the standard for implementing APIs.
The OpenAPI Specification (OAS) defines a standard, language-agnostic interface to RESTful APIs [...] without access to source code, documentation, or through network traffic inspection. https://swagger.io/specification/
Creating standards for APIs allows content to be extracted without being dependent on the language. Swagger is a kind of language in itself.
I won't talk too much about Swagger because that's not the purpose of this article. For those who have never used Swagger, check out this example of SwaggerUI :
Most importantly, it generates a JSON file representing the entire API. It can be shared, but not only. This is the starting point of NSwag.
Before going further, we need to analyze how the architecture is designed, and why clients facilitate data exchange.
Understand how microservices work
Web applications now mainly use a microservices architecture.
For those who have never heard of this term, you should read the article on RedHat to understand all the benefits:
In short, the architecture is no longer composed of a single block that contains all your logic. Each element of your business logic is divided into a microservice that operates independently. The microservice is a subdivision of a part of your logic.
The more independent each microservice is, the easier it is to add a new microservice to your application.
Moreover, one microservice can be dependent on another when it needs to share information.
Let's take an example.
Application X allows a company to sell products online. Currently, the company can manage its users and products. In order to improve the workflow, it needs to create invoices to produce evidence of its sales.
Billing microservice will need to retrieve user information from the
We need to ask ourselves: how do we query
If your first thought is to instantiate the
Users service inside the
Billing service, this is pure nonsense. You are literally creating a huge dependency in your architecture, the opposite of the microservices model!
Remember, the splitting functionality is the key.
Instead, let's make microservices communicate with each other. To do this, each microservice that needs to share data will contain a client (a proxy class) to expose all its endpoints.
Thus, the client of the
Users microservice will be called from the
Billing microservice to query accessible functions such as the one to get the user information
GetUserInformation(int userId) .
Continue with the example. Imagine you need to create
Literally, no development is needed to connect the two services since the interface already exists.
The best part? It is possible to automatically generate the client 😎
Generate your microservice's client with NSwag
Since the exposure of the microservice is already documented by the swagger file, it would make no sense to develop the client to communicate from scratch.
This is where NSwag comes in to save us.
The Swagger/OpenAPI toolchain for .NET, ASP.NET Core and TypeScript.
NSwag is a set of tools mainly used to generate clients and controllers from Swagger/OpenAPI files and vice versa.
Start by building your NSwag configuration file with NSwagStudio
First of all, NSwag integrates interface tools called NSwagStudio to start using the library and configure all the parameters needed for command-line automation.
You can download it for free, and it is open-sourced:
The functionality is quite simple:
- Specify the path to the API Swagger file as OpenAPI Specification
- Check the desired outputs, the CSharp client in this case
- Configure the settings to generate the client you need
- Click the Generate Outputs button and copy the generated class
There is a lot of settings to configure your client. Try to deep dive into it to be sure to produce the class that needs it.
For example, you can specify: the client class name, the namespace, if you want to also generate an interface and more.
You can also easily modify the template which generates the class if you need to add parameters to the constructors for dependency injection, but I will do another article about it later. Let's focus on the client's generation.
Split your API classes inside a separate project
Your project management must include strict rules.
All your POCO (plain old class object) must be moved to another project, independent of the microservice. POCO means all objects used to call the API of your microservice. It should include only the attributes and should not depend on an external framework.
Thanks to this, classes can be shared between the original microservice, the client, and also client-based microservices without duplicating them.
Why do I insist on this?
Additional namespaces can be added in the NSwag configuration file and should be used whenever possible.
Cleaner organization for cleaner code.
Once NSwag is configured, save the file to your client project. The file extension is
.nswag, we will need it later.
Automatize the generation from microservice post-build event
The NSwag library also contains the NSwag.CodeGenerator.CSharp package to generate a client from a SwaggerFile. It is exactly the same package used in NSwagStudio but in command-line, installable from Nuget.
Since the NSwag file is already configured, the only remaining step is to generate the client via CLI.
Use the command
nswag run /runtime:Net60 in the Package Manager console to generate the client from the configuration and the Swagger file.
Don't forget to modify the runtime if necessary.
You can take it a step further by automating the entire process at the post-build event of the microservice project to update the Swagger file and generate the client each time you build the API.
With this automated generation, you will be notified of any changes to the user microservice API. This means that if you add a route, you will be able to call it from the client.
More importantly, if an endpoint is modified by the route, the parameters, or the return object, compilation will no longer be possible on each dependent microservice.
UserClient is fully configured, autogenerated, and can be called with the route as a parameter:
var client = new UserClient("URLOFUSERAPI"); var userInformation = client.GetUserInformation(USERID);
Pretty simple to use now, right?
The microservices architecture will become more and more important within robust applications.
Thinking upstream about the communication between microservices is mandatory to avoid creating dependencies and creating a monster where no one will want to add functionalities without being afraid to make everything collapse.
The use of clients is one of the essentials to properly manage the exchanges between these microservices.
And now you know how to do it properly.