In this article I will talk about our attempt to make a statically typed REST API and save the front-end team from writing code for writing data requests, simplify testing and reduce the number of possible errors.

These tools help us:
- Design and model APIs to standards based on specifications
- Create stable, reusable code for your API in almost any language
- Improve developer experience with interactive API documentation
- Easily perform functional tests on your APIs.
- Create and apply the best API style guidelines in your API architecture
The main idea of the approach is that having the same data types on the client and server, it is more difficult for the developers in the team to make an error on the client, and using the code generation it will not need to be written, maintained and, accordingly, covered with unit tests.
The frontend application will not compile if the command made a mistake in the data types that the REST API accepts / supplies.
Thus, having statically typed code on the client, we can get rid of stupid errors related to types and be sure that our code is fully compatible with the current version of the API.
In order to get all the above advantages of a statically typed API, we need to use a code generator that, according to the OpenAPI specification, can generate type description files, for Typescript these are * .d.ts files.
Our project uses microservice architecture and the entire backend is written in .NET, so we used NSwag to generate API clients for the frontend / backend. This tool allows you to generate OpenAPI documents, which then in turn are already used to generate client code.
Architecture
In general, the code generation process consists of several stages:
- OpenAPI Document Generation
- Generating code using an OpenAPI document
In our case, the backend is written using .Net and the main C # development language is the reason for the choice of tools. We use the same tool called NSwag to generate OpenAPI documents and clients.

Figure 1 Architecture of a solution for generating rest client code
The figure shows:
- Microservice 1 ... N - microservices providing API
- NSwag - generating an OpenAPI document from a microservice API code (a microservice can be written in any language for which there is a tool for generating an OpenAPI document)
- NSwag - generating client API code for OpenAPI documentation (there are many tools you can choose the one that suits your technology stack more)
- OpenAPI doc - OpenAPI documentation generated
- API Client 1 ... N - clients consuming API data (can be implemented on any language that supports the generator for NSwag C # and Typescript)
- SPA 1 ... N - Frontend application, in our case React (NSwag supports client generation for AngularJS / Angular, React (fetch), JQuery (Promise / Callback), Aurelia)
The sequence of actions is as follows:
- Mark the API controller with the corresponding tag / attribute / decorator depends on the language of the API
- Generate OpenAPI API Code Documentation
- Generate client API code using OpenAPI documentation
- Integrate client API code into a data consumer API application
Documentation
In order to be able to generate code, we need to describe the types of accepted / returned values of the API controller, in this example, where the C # language is used, using the SwaggerOperation attribute, we marked a method that will return a list of all the questionnaires on the server, in the client code, a method to get The data will be called GetAllQuestionnaires:
[HttpGet, Route("")] [SwaggerOperation(OperationId = "GetAllQuestionnaires")] [SuccessResponse(typeof(IEnumerable<QuestionnaireViewModel>))] public IEnumerable<QuestionnaireViewModel> Get() { var surveys = _questionnaireRepository.GetAll(); return surveys.Select(QuestionnaireViewModel.ToViewModel).ToArray(); }
Listing 1 Example C # code describing an API method
Then, using NSwag, we automatically generate an OpenAPI document that will contain all the API endpoints that were marked with the corresponding attributes in the backend code.

Figure 2 OpenAPI Document
Thus, we managed to create always up-to-date automatically updated documentation of our API.
Typing
The OpenAPI documentation contains information about the types of data that will be sent / received by the backend controller. Thus, on the front-end side, we can fully rely on the types that the backend supplies to us and not create our own types, but import them from the client code that was generated using the OpenAPI document.
For our example, the document contains information about the QuestionnaireViewModel type (here the specification is presented in HTML form for readability)

Figure 3 Example data model in an OpenAPI document
The next step is to pass this information to the frontend application code.
Code generation
We also use NSwag to generate client API code. At the input, it receives an OpenAPI document and generates client API code in accordance with the specified settings. For the front, next to the received code, we add package.json and send it to our local npm register.
As you can see from the backend code listing (see Listing 1), we marked the controller method with the attribute
[SwaggerOperation(OperationId = "GetAllQuestionnaires")]
The OperationId specified in the C # attribute in our case will become the name of the client method.

Figure 4 Example usage of the generated client API
Also, after generating the client, we received a d.ts file that contains the corresponding descriptions of data types, shown in the figure below.

Figure 5 Example data type description in a .d.ts file
Now, in the frontend application code, you can use data types that are exported from the client’s API code and use auto-completion in the code editor, an example is shown in the figure below.

Figure 6 Example use of client API data type information
All relevant data type validators in Typescript also work.
An example in the figures below.

Figure 7 Example client API data type validation

Figure 8 Example client API data type validation
conclusions
After applying this approach, we received the following advantages:
- Less data type bugs
- Less code, less labor for debugging, testing, support
- Always up-to-date documentation for all API methods for each of the microservices
- Ability to test API automation
- Unified type system for frontend and backend
References