The Challenge: Fixed Base URLs in Auto-Generated API Code

When integrating APIs into Angular applications, developers frequently rely on generators such as Swagger Codegen or OpenAPI Generator to produce TypeScript clients. A common limitation of these generated artifacts is that they ship with a static basePath, which complicates environment-specific configuration.

This guide demonstrates how to inject the basePath at runtime while leaving the generated code untouched. The solution is designed to function across both NgModules and Standalone Components.

The Issue: Static Paths Inside Generated Output

Typical Swagger-generated clients contain a hardcoded value similar to:

protected basePath = 'https://{INSTANCE_NAME}.example.com/api/2.0';
Enter fullscreen mode Exit fullscreen mode

In this snippet, {INSTANCE_NAME} is a fixed string literal, not a placeholder that can be resolved through dependency injection. As a result, changing the target endpoint forces you to manually edit the generated files.

This becomes unmanageable, particularly in projects that consume numerous API services.

The Fix: Registering a Configuration Provider

Step 1: Utilize the Configuration Class

Fortunately, the code generator includes a Configuration class:

export class Configuration {
    basePath?: string;
    constructor(configurationParameters: ConfigurationParameters = {}) {
        this.basePath = configurationParameters.basePath;
    }
}
Enter fullscreen mode Exit fullscreen mode

This presence allows you to register a Configuration object at runtime, sidestepping any need to alter the generated output.

Step 2: Build a Configuration Factory

To make the base path configurable, define a factory function that produces the configuration object:

import { Configuration } from './generated/configuration';  // Adjust the import path

export function configurationFactory(): Configuration {
  return new Configuration({
    basePath: `https://mysite.example.com/api/2.0`
  });
}
Enter fullscreen mode Exit fullscreen mode

The returned Configuration instance carries the basePath that has been resolved from the current environment.

Step 3: Register the Configuration in AppModule

At the application root, invoke ApiModule.forRoot() and pass in the factory:

import { NgModule } from '@angular/core';
import { ApiModule } from './generated/api.module';  // Adjust the import path
import { configurationFactory } from './configuration.factory';  // Import our factory function

@NgModule({
  imports: [
    ApiModule.forRoot(configurationFactory)  // Provide the API configuration
  ],
  bootstrap: [AppComponent]  // Root component
})
export class AppModule { }
Enter fullscreen mode Exit fullscreen mode

This setup guarantees that every API service throughout the application receives the correct basePath from the provider.

Consuming the API in a Standalone Component

For projects using standalone components, forRoot() is unnecessary. Just import ApiModule directly:

import { Component } from '@angular/core';
import { ApiModule } from './generated/api.module';  // Import the API module
import { SomeApiService } from './generated/api/some-api.service';  // Example API service

@Component({
  selector: 'app-standalone',
  standalone: true,
  imports: [ApiModule],  // No need for forRoot()
  template: `<p>Standalone Component</p>`
})
export class StandaloneComponent {
  constructor(private apiService: SomeApiService) {
    this.apiService.someApiMethod().subscribe(response => {
      console.log(response);
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Because the Configuration is already available at the module level, API services function without any additional wiring.

Why This Strategy Is Effective

Generated files remain untouched
All configuration is centralized within AppModule
Compatible with NgModules and Standalone Components
Simplifies adding multiple API integrations

By taking advantage of Angular's dependency injection system alongside ApiModule.forRoot(), the API clients become flexible, easy to maintain, and consistent across the entire application.

Final Thoughts

When working with Swagger Codegen in an Angular context, avoid manual edits to generated files. Instead, rely on ApiModule.forRoot() and provide a dynamic Configuration object.

The outcome is an API layer that is environment-agnostic, easy to scale, and straightforward to maintain.

Share your experiences! Have you encountered similar hurdles while integrating Swagger APIs into your Angular projects? 🚀