OpenAPI in Angular: From Manual Services to Generated Goodness (and a Lot of Refactoring)
“Firmly grasp it in your hand!” - Patrick Star
The Motivation: From Repetitive Code to a Better Way
We already had OpenAPI set up in the backend. So naturally, I asked myself — why am I still writing Angular services by hand?
Creating HTTP services manually was becoming tedious and error-prone. Not to mention, every change in the backend required me to double-check if the frontend stayed in sync. I wanted something cleaner. Smarter. More streamlined. That’s when I decided to bring OpenAPI to the frontend too.
The Setup: Generating Angular Code with OpenAPI
Enter openapi-generator-cli
.
Before anything else, you’ll need to have your OpenAPI spec (in my case, my-openapi.yaml
) available in your frontend project — either committed directly or pulled in through your pipeline.
This file is the blueprint that tells the generator what to build.
Once that’s in place, I ran:
npx openapi-generator-cli generate -i my-openapi.yaml -g typescript-angular -o libs/api
This generated a fully-typed Angular client based on our backend’s OpenAPI definition. Clean. Structured. No more manually writing request/response interfaces or services.
To make it easily reusable, I added this to my package.json
scripts:
"scripts": {
"generate-api": "npx openapi-generator-cli generate -i my-openapi.yaml -g typescript-angular -o libs/api"
}
So anytime the backend changes, I can just regenerate the frontend with one command. No fuss.
The Configuration: Making It Work with Angular
Next, I had to configure Angular to use the generated code properly.
You’ll need to provide a custom Configuration
so the generated services know where your API lives:
{
provide: Configuration,
useFactory: (): Configuration => {
const params: ConfigurationParameters = {
basePath: environment.baseUrl
};
return new Configuration(params);
}
}
Pro tip: Don’t generate inside src/
.
I placed everything in a libs/api
folder to keep things modular and outside the core app.
The Great Refactor: Painful but Worth It
This is where things got real.
I started replacing all our old manually written services with the auto-generated ones. It was painful at first — services had different naming, method signatures weren’t exactly the same, and mocking in tests needed updates too.
Example: Before, I used a custom service to fetch attachments. Now I just call:
this.attachmentsService.getAllAttachments()
Much cleaner. Less boilerplate. More consistency.
Testing Adjustments
Tests needed some love too.
I replaced a bunch of old MockProvider
with simpler mocks using the generated interfaces.
Example:
{
provide: AttachmentsService,
useValue: { getAllAttachments: jest.fn() }
}
Simple. Maintainable. And way easier to understand when revisiting tests later.
Syncing OpenAPI Across Teams
One thing I had to think about was how to keep the OpenAPI spec in sync between backend and frontend. If you’re working in a monorepo — lucky you. But if your backend and frontend are in separate repos (like ours), there are a few ways to handle it:
-
Option 1: Commit the OpenAPI YAML directly into the frontend repo.
Simple and works well if the spec doesn’t change too often. -
Option 2: Automate fetching the spec in your CI/CD pipeline.
For example, have a script that pulls the latestopenapi.yaml
from the backend before generating the client. -
Option 3: Use a shared package or Git submodule.
Slightly more complex but very reusable across multiple frontends.
Whatever you pick, just make sure you’re not manually copying files around. That defeats the purpose of automation.
Gotchas & Gains
Let’s be real — bringing OpenAPI to Angular isn’t just plug-and-play. There were a few bumps on the road:
-
Casing and naming conventions
The generated models didn’t always match our style. Thankfully, you can tweak this with options like--additional-properties=modelPropertyNaming=camelCase
. -
Enum weirdness
Enums behaved a bit unexpectedly — especially when the backend used patterns that didn’t translate neatly to TypeScript. -
Circular references
These can mess with the generator in complex specs. Keeping the OpenAPI doc clean and well-structured helped. -
Massive PRs
The initial PR introducing the generated client? Huge. But 90% of it was boilerplate. Reviewers just needed coffee and patience.
Despite the hiccups, it was absolutely worth it. Here’s why:
- ✅ No more duplicating DTOs between frontend and backend
- ✅ Backend changes? Just regenerate — no guesswork
- ✅ Strongly typed services made refactors way safer
- ✅ Test mocks became leaner and easier to write
- ✅ This setup now lives in every project I touch
Yes, the refactor took time. But it simplified the future. I’d do it all over again — and sooner.
Final Thoughts
Bringing OpenAPI to Angular wasn’t just a technical upgrade — it was a mindset shift.
We talk a lot about automation, DevOps, and building smarter systems. But this is that same philosophy applied at the code level. Why keep writing the same HTTP service over and over when your tools can generate it for you — strongly typed, documented, and ready to go?
By introducing OpenAPI to the frontend, I made Angular feel like a first-class citizen in our API ecosystem. I stopped duplicating DTOs, stopped guessing request shapes, and started focusing on the things that actually need a human brain: logic, UX, and problem-solving.
Yes, the refactor was painful. Yes, it took time to get the mocks right and the tests stable again. But now? Future me is thankful.
So if you’re staring at yet another service you have to hand-code — maybe that’s the sign. Automate what can be automated. Let the machines do the boilerplate. And use your time for the parts that matter.