DEV Community

Cover image for How to Defeat the Chaos of Manual Backend Contracts: Automating Models in Angular
Art Stesh
Art Stesh

Posted on

How to Defeat the Chaos of Manual Backend Contracts: Automating Models in Angular

Greetings,

today I decided to talk about how automatic generation of models and services on the frontend can simplify the development process.

The problem of interaction between the frontend and backend is one of the biggest headaches for developers. Data format mismatches, outdated documentation, and integration errors inevitably slow down the development process. However, modern approaches make it possible to minimize these challenges.

One technology that has proven effective for such tasks is automatic code generation from OpenAPI specifications.


The Problem with Contracts: Why "Gentlemen’s Agreements" Always Lead to Pain

In the early stages of developing small applications, frontend and backend teams often communicate something like: “Well, let’s agree like normal people! The JSON will look like this, ok?” — “Ok.” It sounds nice, like a promise to meet at the same place in five years.

A month later — the frontend is already tied to the old contract, while the backend decided to "slightly improve" things, but no one can dispute the original agreements anymore because, of course... they weren’t written down. This leads to a classic scenario:

Backend: “Well, you know how we do things in REST, we replaced userId with clientUuid and moved personal data into a separate model. It's all logical anyway.”

Frontend: sounds of panic and frustration

QA: “The backend works perfectly, I’ve logged 100,500 bugs against the frontend team.”

Manager: “My presentation is in two hours; is it really that hard to fix this?”

It's at moments like these when you realize: it’s better to spend a couple of hours generating contracts than endlessly "syncing up" with another developer on Slack.

Here are the main problems teams face:

  • Inconsistent request and response formats. The API is constantly expanding, and the gap between the frontend and backend continues to grow.
  • Unpredictable changes. Backend developers add functionality, breaking the frontend — often unintentionally.
  • Lack of up-to-date documentation. No one informs the team in time when an endpoint's behavior is modified.
  • Manual validation. Sending requests manually, analyzing errors — all of this wastes an enormous amount of time.

The Core Idea: Automation Instead of Routine

When the frontend is developed based on a backend with a defined API (e.g., OpenAPI), it's entirely possible to eliminate the manual creation of interfaces, services, and data modeling. Instead, everything is done automatically using code generators.

Why Is This Important?

  1. Data Accuracy: Automatic generation of interfaces eliminates the chance of type errors.
  2. Faster Development: Instead of creating models manually, developers receive ready-to-use code immediately.
  3. Easier Updates: When a backend contract changes, it’s enough to update the OpenAPI specification and regenerate the files — everything on the frontend will update automatically.

4. Reduced Developer Workload: Teams can focus on functional logic instead of wasting time syncing data.

How to Implement This in Projects?

1. Swagger Integration and Documentation Generation

The first key task is to make the API “self-explanatory.” This is important for the frontend (there’s no time to dig into the backend code to figure out how to send a request) and for the testers.

We set up automatic documentation generation, which is hosted on Swagger UI at the /api-docs endpoint.

In C#, connecting it comes down to installing the


 and

 ```Swashbuckle.AspNetCore.Annotations```

 libraries and adding a few lines of code in Program.cs (or Startup.cs):



```textmate
services.AddSwaggerGen(c =>
        {
            c.SwaggerDoc("v1", new OpenApiInfo { Title = "Api", Version = "v1" });
            var currentAssembly = Assembly.GetExecutingAssembly();
            var xmlDocs = currentAssembly.GetReferencedAssemblies()
                .Union(new[] { currentAssembly.GetName() })
                .Select(a => Path.Combine(Path.GetDirectoryName(currentAssembly.Location), $"{a.Name}.xml"))
                .Where(f => File.Exists(f)).ToArray();
            xmlDocs.ToList().ForEach(d =>
            {
                c.IncludeXmlComments(d);
            });
        });
        ...

app.UseSwagger();
app.UseSwaggerUI();
Enter fullscreen mode Exit fullscreen mode

For other programming languages, integrating Swagger is not a challenge — the vast number of examples available online for practically every use case makes it quick and easy to set up in nearly any tech stack.

Main Advantages of Swagger:

  • A convenient visual interface for exploring endpoints.
  • Easy integration into CI/CD for generating up-to-date documentation.
  • Enables simple access for all team members (frontend, testers, analysts) to the current state of the API.

2. Generating Frontend Models with ng-openapi-gen

Considering that the foundation of our frontend is Angular, for generating models and services based on OpenAPI schemas, we chose the library ng-openapi-gen. This tool, in a semi-automated manner, creates Angular services and models from the OpenAPI specification, freeing developers from the need to manually describe data structures and requests.

Preparing the OpenAPI Specification

To manage the generation process, a json file is created (in any location), one for each data source (the application can interact with multiple different APIs):

{
  "$schema": "../../../../node_modules/ng-openapi-gen/ng-openapi-gen-schema.json",
  "input": "https://localhost:5001/swagger/docs/v1/backend-name", // backend docs URL
  "output": "src/app/api/backend-name", // destination for the generated models
  "ignoreUnusedModels": false,
  "indexFile": true,
  "removeStaleFiles": true,
  "skipJsonSuffix": true,
  "module": "ApiModule",
  "configuration": "ApiConfiguration"
}
Enter fullscreen mode Exit fullscreen mode

Code Generation

Add a new script to


:


```json
"openapi-gen-backend-name": "ng-openapi-gen -c <path to the config file from the previous step>/config.json"
Enter fullscreen mode Exit fullscreen mode

That's it! To generate services and models, simply run:

npm run openapi-gen-backend-name
Enter fullscreen mode Exit fullscreen mode

Once the command is executed, the specified directory (in our case, src/app/api/backend-name) will contain the automatically generated models, services, and the ApiModule, which can be integrated into your application.

...

@NgModule({
  imports: [
    ...
    ApiModule.forRoot({rootUrl: 'https://localhost:5001'})
  ]
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

Using the Models and Services

Generated models and services provide strict typing and a convenient interface for API requests. For example, the User model and the UserService service are generated automatically:

Example of Generated Models:
export interface User {
  id: number;
  name: string;
}
Enter fullscreen mode Exit fullscreen mode
Example of Generated Service:
...

@Injectable({
  providedIn: 'root'
})
export class UserService {
  constructor(private http: HttpClient) {
  }

  getUsers(): Observable<User[]> {
    return this.http.get<User[]>('/users');
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, the service can be used in any component of your Angular application:

Often, the code of the generated services may seem odd and not particularly user-friendly, but the idea is that we don’t need to focus on it. All we care about is the contract itself.

Example of Usage:
import {Component, OnInit} from '@angular/core';
import {User, UserService} from '../api';

@Component({
  selector: 'app-user-list',
  template: `
    <div *ngFor="let user of users">
        {{ user.name }} (ID: {{ user.id }})
    </div>
  `
})
export class UserListComponent implements OnInit {
  users: User[] = [];

  constructor(private userService: UserService) {
  }

  ngOnInit(): void {
    this.userService.getUsers().subscribe((data) => {
      this.users = data;
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Alternatives

It’s worth noting that using OpenAPI is not limited to Angular or frontend development. The approaches outlined here can scale across many popular technologies. For example, in React and TypeScript environments, libraries like openapi-typescript-codegen and swagger-typescript-api are widely used to generate strictly typed API clients. In Python and Java ecosystems, tools like openapi-generator help save development time for both server and client applications, generating code tailored for frameworks such as Flask, Django, Spring Boot, or Kotlin.

This proves that the main advantage of OpenAPI is its versatility. Regardless of the programming language or framework, OpenAPI unites frontend, backend, and QA teams around a single contract, minimizing human error and integration issues. Whether your team is working with web technologies, mobile platforms, or server applications, OpenAPI and automatic generation tools help standardize processes and accelerate development.

Tool Languages/Frameworks Features
openapi-typescript-codegen React, TypeScript A compact generator for clients with types.
openapi-generator Vue, Svelte, Python, Java, Kotlin, etc. A powerful, multi-platform tool.
swagger-typescript-api React, Node.js, TypeScript Lightweight and fast generator with CLI.

Recommendations for Tool Selection

  • If you are using Angular, your best choice is ng-openapi-gen.
  • For React/TypeScript, pick openapi-typescript-codegen or swagger-typescript-api. Both libraries work well for different scenarios.
  • For complex or multi-language projects, use openapi-generator, as it supports a broader range of platforms and languages.
  • Working with frameworks like Django or Spring? openapi-generator can even generate server-side code.

3. Integrating OpenAPI into CI/CD

A major focus should be placed on automating contract validation at the pipeline level to avoid desynchronization between the frontend and backend.

What to Add to CI/CD:

  • Automatic documentation generation (Swagger on the backend) for every deployment.
  • Specification validation using tools like swagger-cli or openapi-cli.
  • Synchronization of frontend models during the build process (while high-level validations are unlikely here, the mere fact of invalidating the code—and consequently preventing compilation—in case of untracked backend changes significantly reduces reaction time).

Results: What Did OpenAPI Implementation Achieve?

After several months of active use, we observed the following improvements:

  • Fewer Errors: Thanks to strict typing, problems are identified before requests even run.
  • Faster Development: Automating model and documentation generation reduced the time required for updating and integrating APIs.
  • Transparency: Swagger UI and generated types simplified API structure comprehension for newcomers to the team.
  • Stable Collaboration: QA teams gained access to documentation through Swagger, and frontend developers started working with clear, readable interfaces.

Conclusion

Establishing seamless collaboration between the frontend and backend is no easy task, especially as your project grows and contracts multiply. Automatic generation of frontend models and services helps eliminate routine work, reduce desynchronization risks, and speed up development. Instead of manually writing interfaces or dealing with unverified contracts, you gain a single source of truth, synchronized with just a couple of commands.

The best part is that you don’t need to completely overhaul your project or development approach. You can start small: set up model and service generation for one module or a key API. Gradually, your process will stabilize, your team will work faster, and the number of bugs at the server-client intersection will noticeably decrease.

Using tools like OpenAPI or similar solutions only simplifies this process, providing developers with an efficient and convenient way to maintain order in the contract layer. By introducing automation step by step, you’ll not only reduce routine tasks but also make development significantly more comfortable.

There is an excellent way to confirm this—try it for yourself. And soon, you may find it hard to imagine API development without these kinds of solutions.

Top comments (0)