DEV Community

Cover image for Simplifying Angular Development with Swagger: A Step-by-Step Guide
Daniel Sogl
Daniel Sogl

Posted on

Simplifying Angular Development with Swagger: A Step-by-Step Guide

As an Angular trainer and consultant, I've encountered numerous Angular projects. Many developers express their dislike for writing boilerplate code, such as with Redux, or when defining API-Services alongside response models. In this blog post, I'll demonstrate how to use Swagger and its API generator to create Angular HTTP-Services, API-DTOs, and configure the endpoint based on your deployment environment.

What is Swagger

Swagger is a fantastic open-source toolset that's perfect for developing and describing RESTful APIs. It offers you a user-friendly interface to understand a service's capabilities without needing to dig into the code. It even provides a way for you to interact directly with the API, which means you get to test its functionality.

What makes Swagger really stand out is how it streamlines the process of defining and documenting APIs. It offers a centralized, language-agnostic definition of an API. This means that both you and machines can easily discover and understand the capabilities of the service. Thanks to Swagger, you can avoid getting bogged down in extensive documentation or wasting time explaining how your API works, because the API definition is self-explanatory and easy to understand.

Swagger offers more than just a user-friendly interface for exploring APIs. It also provides multiple generators that can produce code typically written by hand. As an Angular developer, this blog post will focus on the typescript-angular generator.

How to Set Up the Swagger Generator

I will guide you through the process of setting up the typescript-angular generator. I will define a separate Angular library to keep the generated code away from our application code. I will also create a script that pulls the current swagger config so you don't have to check the current version by yourself and manage the required swagger definition file in your Angular repository. Last but not least, I will show you how to configure the base path of the endpoint based on your environment. If you want to take a closer look into the presented code you can also check out the demo project published on GitHub.

Setting Up the Library

First of all, I will create a separate Angular Library with the help of the Angular CLI, so we can keep the generated code away from our application code.

ng g lib api
Enter fullscreen mode Exit fullscreen mode

This will generate a library called api inside a folder named projects. It also modifies our tsconfig.json file by adding a path to the dist folder. Because we don't want to build the API lib each time we generate the code, we can instead point to the public_api.ts file inside the api lib src folder.

{
  "compileOnSave": false,
  "compilerOptions": {
    "paths": {
      "api": ["./projects/api/src/public-api.ts"]
    },
    ...
  },
}
Enter fullscreen mode Exit fullscreen mode

This helps us later to import the generated code from our library without referencing to the absolute/relative paths as shown below.

import { Configuration, ConfigurationParameters } from 'api';
Enter fullscreen mode Exit fullscreen mode

Setting Up the Generator

To generate our API, I will first add a dev dependency to the project. Under the hood, the generator will execute a Java library, so it is also necessary to install the JDK on your system. I recommend using Homebrew on macOS.

brew install openjdk
...
npm install -D @openapitools/openapi-generator-cli@latest
Enter fullscreen mode Exit fullscreen mode

After we've installed all the required dependencies, we can create a definition file to configure the generated code.

touch openapitools.json
chmod -R 777 openapitools.json // can be required later
Enter fullscreen mode Exit fullscreen mode

After creating the file, I define the generator configuration. It's possible to define as many generators as needed in your project. So, if your application accesses multiple endpoints or microservices, just extend the config as needed. In the shown example below, I'm defining the output path of the generator as well as some useful parameters. These are just my go-to parameters. If you'd like to dig deeper into the generator, I recommend you read through the documentation. The configuration may vary depending on when you're reading this article.

{
  "$schema": "./node_modules/@openapitools/openapi-generator-cli/config.schema.json",
  "spaces": 2,
  "generator-cli": {
    "version": "7.4.0",
    "generators": {
      "pet-store-api": {
        "generatorName": "typescript-angular",
        "glob": "swagger.json",
        "output": "projects/api/src/lib",
        "additionalProperties": {
          "apiModulePrefix": "PetStore",
          "withInterfaces": "true",
          "fileNaming": "kebab-case",
          "useSingleRequestParameter": "true",
          "supportsES6": "true",
          "stringEnums": "true"
        }
      }
    }
  }
}

Enter fullscreen mode Exit fullscreen mode

To execute the generator, I create another file. The script not only executes the generator but also downloads the latest swagger definition file from the web and cleans up the library folder. This prevents us from committing deleted files to our git repository.

touch generate-api.sh
chmod -R 777 generate-api.sh
Enter fullscreen mode Exit fullscreen mode
#!/bin/bash

# download the latest swagger definition file
curl -o swagger.json https://petstore.swagger.io/v2/swagger.json
# remove the existing api folder
npx rimraf projects/api/src/lib
# generate the api client
npx openapi-generator-cli generate --generator-key=pet-store-api
# delete the swagger definition file
rm ./swagger.json
Enter fullscreen mode Exit fullscreen mode

I also recommend adding a custom script to your package.json to execute the script using the npm run command.

{
  "name": "ng-swagger-generator",
  "version": "0.0.0",
  "scripts": {
      ...
    "generate:pet-store-api": "./generate-api.sh"
  },
  ...
}
Enter fullscreen mode Exit fullscreen mode

Now it's time to execute the generator using the previously defined script

npm run generate:pet-store-api
Enter fullscreen mode Exit fullscreen mode

When you take a closer look at the generated code, you will find two new folders inside the API project. Inside the api folder, you will find the generated services that we will later import to call our backend. Inside the model folder, you will find all the generated data models (in this case, interfaces).

generated api

Only one last step is required to be able to use the create api library in our Angular application. Adjust the existing public_api.ts file to export the index.ts file from the generate code.

/*
 * Public API Surface of api
 */

export * from './lib';
Enter fullscreen mode Exit fullscreen mode

Using the Generated API Code

As I write this post, the Swagger Angular generator does not support the new standalone API out of the box, so I had to wrap the import using the importProvidersFrom helper function. However, as you can see, the configuration of the generated library is simple. First, define a configuration factory. In my example, I am using the static environment file. For larger applications, I recommend using a configuration service that fetches the API endpoints from the backend to maintain flexibility. After that, you need to import the generated PetStoreApiModule and provide the previously defined config factory. If your backend requires basic authentication, you can also provide the username and password parameters. Also don't forget to provide the HttpClient by adding the provideHttpClient function.

import { ApplicationConfig, importProvidersFrom } from '@angular/core';
import { provideRouter } from '@angular/router';

import { routes } from './app.routes';
import { Configuration, ConfigurationParameters, PetStoreApiModule } from 'api';
import { provideHttpClient } from '@angular/common/http';
import { environment } from '../environments/environment';

export function apiConfigFactory(): Configuration {
  const params: ConfigurationParameters = {
    basePath: environment.basePath,
    // if needed
    password: 'password',
    username: 'user',
  };
  return new Configuration(params);
}

export const appConfig: ApplicationConfig = {
  providers: [
    provideRouter(routes),
    provideHttpClient(),
    importProvidersFrom(
      PetStoreApiModule.forRoot(apiConfigFactory)
    ),
  ],
};

Enter fullscreen mode Exit fullscreen mode

Now, we can import the also-generated PetService to communicate with our backend. We can also use the generated data models to work in a type-safe manner.

import { Component, inject } from '@angular/core';
import { RouterOutlet } from '@angular/router';
import { Pet, PetService } from 'api';

@Component({
  selector: 'app-root',
  standalone: true,
  imports: [RouterOutlet],
  templateUrl: './app.component.html',
  styleUrl: './app.component.css'
})
export class AppComponent {
  private readonly client = inject(PetService);

  constructor() {
    this.client.getPetById({ petId: 1 }).subscribe((pet: Pet) => {
      console.log(pet);
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Thats it! You now have a generated API-library with pre defined Http-Services and data models that can be used inside your application code without writing any single line of code again!

Conclusion

Utilizing Swagger streamlines Angular development by automating the creation of HTTP-Services and API-DTOs, drastically reducing manual coding and minimizing errors. It's important to note, however, that leveraging Swagger effectively does require upfront effort in the backend to provide comprehensive Swagger documentation. Despite this, the investment pays off by significantly easing frontend development tasks.

I encourage you to integrate Swagger into your Angular projects and share your insights. How did it impact your development workflow? Were there any challenges or valuable lessons learned along the way? Your experiences not only enrich our collective knowledge but also guide our exploration of new topics.

What other Angular tools or strategies would you like to see discussed? Your suggestions are crucial for shaping our future content.

Happy coding, and I look forward to your stories and ideas!

Top comments (2)

Collapse
 
jangelodev profile image
João Angelo

Hi Daniel Sogl,
Your tips are very useful.
Thanks for sharing.

Collapse
 
danielsogl profile image
Daniel Sogl

Thanks for your feedback!