From Monolith to Plugin Architecture: Refactoring nx-plugin-openapi
Introduction
What started as a single Nx plugin for generating API clients from OpenAPI specifications has evolved into a flexible, extensible plugin ecosystem. This article documents the architectural refactoring that transformed @lambda-solutions/nx-plugin-openapi from a tightly-coupled monolith into a modular plugin-based system supporting multiple code generators.
The refactoring introduced three new packages:
| Package | Purpose |
|---|---|
@nx-plugin-openapi/core | Plugin infrastructure, executor, auto-installation |
@nx-plugin-openapi/plugin-openapi | OpenAPI Generator integration |
@nx-plugin-openapi/plugin-hey-api | hey-api/openapi-ts integration |
Why the Refactoring?
The original @lambda-solutions/nx-plugin-openapi was built around a single code generator. While functional, it had limitations:
- Tight coupling - The executor directly spawned OpenAPI Generator CLI
- Hard-coded options - 30+ generator-specific options baked into the schema
- No extensibility - Using a different generator meant forking the project
Users increasingly wanted alternatives like hey-api for modern TypeScript generation with better type safety and tree-shaking.
The New Architecture
┌─────────────────────────────────────────────────────────────┐
│ @nx-plugin-openapi/core │
│ Executor, Plugin Loader, Auto-Installation │
└─────────────────────────────────────────────────────────────┘
│
┌───────────────────┼───────────────────┐
▼ ▼ ▼
┌──────────────────┐ ┌──────────────────┐ ┌──────────────────┐
│ plugin-openapi │ │ plugin-hey-api │ │ Custom Plugins │
│ (OpenAPI Gen) │ │ (hey-api) │ │ (Your own!) │
└──────────────────┘ └──────────────────┘ └──────────────────┘
The core package provides the Nx executor and handles plugin loading. Each plugin wraps a specific code generator. Users select which plugin to use via configuration.
Configuration Changes
Before (Legacy)
{
"targets": {
"generate-api": {
"executor": "@lambda-solutions/nx-plugin-openapi:generate-api",
"options": {
"inputSpec": "apps/api/openapi.yaml",
"outputPath": "libs/api-client/src",
"configFile": "openapi-config.json",
"apiNameSuffix": "Client"
}
}
}
}
After (Plugin Architecture)
{
"targets": {
"generate-api": {
"executor": "@nx-plugin-openapi/core:generate-api",
"options": {
"generator": "openapi-tools",
"inputSpec": "apps/api/openapi.yaml",
"outputPath": "libs/api-client/src",
"generatorOptions": {
"configFile": "openapi-config.json",
"apiNameSuffix": "Client"
}
}
}
}
}
Key differences:
- New
generatorfield selects which plugin to use - Generator-specific options moved to
generatorOptions - Same structure works with any plugin
Switching to hey-api
{
"targets": {
"generate-api": {
"executor": "@nx-plugin-openapi/core:generate-api",
"options": {
"generator": "hey-api",
"inputSpec": "apps/api/openapi.yaml",
"outputPath": "libs/api-client/src",
"generatorOptions": {
"client": "fetch",
"plugins": ["@hey-api/schemas"]
}
}
}
}
}
Key Features
Auto-Installation
Missing plugins are automatically installed when needed:
$ nx run my-lib:generate-api
Plugin '@nx-plugin-openapi/plugin-openapi' is not installed.
Would you like to install it using npm? (y/n) y
Installing @nx-plugin-openapi/plugin-openapi using npm...
Successfully installed @nx-plugin-openapi/plugin-openapi
Starting OpenAPI code generation...
This only happens in interactive terminals, not in CI environments.
Multiple Specifications
Both plugins support generating from multiple specs in one target:
{
"inputSpec": {
"users-api": "specs/users.yaml",
"products-api": "specs/products.yaml"
},
"outputPath": "libs/api-clients/src"
}
Each service generates to its own subdirectory.
Choosing a Generator
| Generator | Best For |
|---|---|
openapi-tools | Angular services, multi-language support, mature ecosystem |
hey-api | Modern TypeScript, excellent type safety, tree-shaking, fetch/axios clients |
You can use different generators for different projects in the same monorepo.
Git History
The refactoring happened over 40+ commits starting September 11, 2025:
| Commit | Description |
|---|---|
f70ff9d | Initial plugin architecture |
c55282c | Added hey-api plugin |
54c150f | Integrated auto-installer |
d1fde23 | Updated documentation |
The legacy @lambda-solutions/nx-plugin-openapi remains functional for backward compatibility.
Creating Custom Plugins
The architecture supports custom plugins for any OpenAPI code generator. Plugins implement a simple interface with a name and generate() method. See the Creating Custom Plugins Guide for details.
Lessons Learned
- Design for extension - The plugin interface is minimal: only
nameandgenerate()required - Graceful degradation - Auto-install skipped in CI, fallbacks for local dev
- Backward compatibility - Legacy package still works, no forced migration
- Document the contract - Clear interfaces enable community contributions
Conclusion
The refactoring transformed a single-purpose tool into a flexible ecosystem. Teams can now choose the generator that fits their needs—or build their own—while benefiting from shared Nx integration.
Resources: