Application client API code generation based on dart openapi generator package

Hello! My name is Daniel, I have been working in mobile development for about three years. When I started working on commercial projects using Flutter and Dart, I had to spend many hours implementing methods for calling REST API project backend. Then I thought about how to optimize the writing of the code of services that work with the remote server of the customer. This allowed me to reduce labor costs and development time by almost 10 times, and the client to receive the finished product faster.

In this article, I will consider the feasibility and practical experience of code generation for client applications written in Flutter, REST API using libraries such as openapi_generator and swagger_dart_code_generator.

The practical meaning of using code generation API

There are several meanings and useful practices for applying code generation API based on a file describing query architecture (methods for receiving/sending messages and data schemas) to the backend server. Usually any project starts with the technical requirements of the customer and architectural design. Already at this stage, we can think about how we can optimize business logic integration application backend to its client.

Usually in such projects there is a phased work. First, the backend team develops the functionality of accessing the API, and only then the team of developers of the client side of the application connects. This approach delays the work on the project, as it excludes the parallel development of the client and server parts at the initial stage of the project. Subsequent changes in the description of already written methods can also entail certain difficulties and additional labor costs.

What does the approach of designing the architecture of client-server interaction and subsequent code generation offer?

The answer follows from the question itself. With this approach, we can optimize development API services on both the client and the backend. Based on the same YAML-file with a description of methods and data schemes, we can generate a 100% working service for interacting with the application server. In an alternative scenario, the programmer would repeat the same identical actions. In this scenario, the occurrence of errors is not ruled out, because it is tedious and boring.

When using code generation, the backend development team can use this file to generate and develop API endpoints. This negotiation suggests that the number of errors between the client and the server will tend to zero. It also allows you to start the development process of the client and server at the same time, in parallel. Changes in the architecture of interaction between the client and the server in this application will not require the same labor costs as in the first option.

This approach also has obvious disadvantages. Typically, the code that has been generated using such libraries is larger than the code written by hand by programmers. If the packagers stop supporting these solutions, additional work will need to be done to integrate another solution.

Technical implementation

Before you start generating the client API, you need to draw up requirements for it and think over the architecture of the interaction between the server and the client. In this article, I will omit this item and take a description Open API generator.

This library is an SDK implementation of the openapi client for generating code for an application that is developed using the Dart programming language. With this library, you can create client-side OpenAPI packages written in Dart using your OpenAPI specification (usually a yaml file) right inside a project written with Dart/Flutter.

In order to use this library, you need to add dependencies to your project in pubspec.yaml.

Add these packages to the dependencies category:

●	built_collection
●	openapi_generator_annotations

Add these packages to the dev_dependencies category:

●	build_runner
●	openapi_generator
●	openapi_generator_cli

Next, we need to add a yaml file describing our API. I placed it in the root folder of the project under the name petstore_api.yaml. And I created an annotation file in the folder with the project models, where the name and links to the yaml file and the output folder are defined, in which the generation files will be placed.

import 'package:openapi_generator_annotations/openapi_generator_annotations.dart';


/// This way we can add yaml files to generate API clients based on dio
@Openapi(
  additionalProperties: AdditionalProperties(pubName: 'petstore_api'),
  inputSpecFile: 'petstore_api.yaml',
  generatorName: Generator.dio,
  outputDirectory: 'api/petstore_api',
)
class PetStoreApi extends OpenapiGeneratorConfig {}

I chose Dio as the network generation option, as it is recommended by the creators of the package.

After setting up the project, you must use the command “flutter packages pub run build_runner build --delete-conflicting-outputs” in order to run the generator. After executing this command, a new api folder will appear in our project repository with the contents of the petstore_api package. To use it further in the project, we need to add the dependency to our pubspec.yaml under the dependencies category.

petstore_api:
    path: ./api/petstore_api

To demonstrate how we can further use our client API, I’m creating a PetstoreNetworkService. In the constructor, I call the PetstoreApi to get work objects to call the API. Also at this stage, you can add any Dio spoilers. I added Dio Logger.

Also defined the fields of the library API objects below:

  late final PetApi petApi;
  late final StoreApi storeApi;
  late final UserApi userApi;
PetstoreNetworkService() {
    // Getting a reference to the Generate API
    final generatedAPI = PetstoreApi();

    // Adding additional dio interceptors for data logging
    generatedAPI.dio.interceptors.addAll([
      dioLoggerInterceptor,
    ]);

    // Getting an instances to call the pet store API
    petApi = generatedAPI.getPetApi();
    storeApi = generatedAPI.getStoreApi();
    userApi = generatedAPI.getUserApi();
  }

Now we can access any method of our API through the methods of the petApi , storeApi and userApi objects.

Future<Pet?> getPetById(int id) async =>
      petApi.getPetById(petId: id).then((result) {
        return result.data;
      }).catchError((Object error) {
        // Do something on error
      });

All data models were also generated based on their description in the yaml file of our Petstore API.

Swagger dart code generator

Package SwaggerDartCodeGenerator is a code generator that looks for *.swagger/*.yaml/*.json files describing the client’s API and generates Dart files based on the API schemas. Model code generation in this library is built on the Chopper and JsonAnnotation packages, which can be customized to suit your needs.

In order to use this library, you need to add dependencies to your project in pubspec.yaml.

Add a package to the dependencies category:

●	chopper: ^5.0.0

Add these packages to the dev_dependencies category:

●	build_runner: ^2.1.10
●	  chopper_generator: 5.0.0+1
●	  json_annotation: ^4.4.0
●	  json_serializable: ^6.1.4
●	  swagger_dart_code_generator: ^2.8.1

Next, I created the api_files folder in the root of the project, where I placed the API description file – petstore_api.swagger.

If you don’t have a .swagger file, you can take the API description in json format and change the file’s permission to .swagger.

To configure API generation through the SwaggerDartCodeGenerator package, you need to add the build.yaml file to the project root. In my example, I used a similar configuration:

targets:
  $default:
    sources:
      - lib/**
      - swaggers/**
      - swaggers2/**
      - api_files/**
      - swagger_examples/**
      - $package$
    builders:
      chopper_generator:
        options:
          header: "//Generated code"
      swagger_dart_code_generator:
        options:
          input_folder: "api_files/"
          output_folder: "lib/swagger_generated_api/"
          override_equals_and_hashcode: true

Next, after setting up the project, you need to use the command “flutter packages pub run build_runner build --delete-conflicting-outputs” in order to run the generator. After executing this command, a new swagger_generated_api folder will appear in our repository in the lib project folder, containing petstore_api, the client of which is implemented using the Chopper package and the JsonSerializable model. To further use our API in the project, you need to create a service or repository file where we will access the methods of our API. I created a file with the implementation of the PetstoreNetworkService class, in the constructor of which I call the PetstoreApi to get the object of the implementation of the methods for calling our API.

PetstoreNetworkService() {
    // Creating an instances to call the pet store API
    petApi = PetstoreApi.create();
  }

  late final PetstoreApi petApi;

Now we can access any method of our API through the methods of the petApi , storeApi and userApi objects.

Future<Pet?> getPetById(int id) async =>
      petApi.petPetIdGet(petId: id).then((result) {
        return result.body;
      }).catchError((Object error) {
        // Do something on error
      });

conclusions

Using code generation for the client API can significantly reduce the time for developing an application and setting up its work with the network. I would recommend using the package Open API generator in large projects, when it is important to fulfill the following requirements:

  • The API can be file-shared into different services/repositories using the Dio client.

  • the implementation of your API can be separated from the main project into a separate package.

A package SwaggerDartCodeGenerator choose when you do not need the benefits of the Openapi generator package and it is important that the generated code be as compact as possible.

Useful links:

https://pub.dev/packages/openapi_generator

https://pub.dev/packages/openapi_generator_annotations

https://pub.dev/packages/openapi_generator_cli

https://pub.dev/packages/swagger_dart_code_generator

https://gitlab.com/Waltrail/api-generation-example

Thank you for your attention!

We also publish useful materials for developers in our social networks – VC and Telegram.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *