I would like to enable Swagger for API interfaces, shared between NestJS and Angular app within an Nx monorepo. Is there a consistent and not workaround-looking way?
These are the approaches I haven't succeeded with:
Approach 1. Apply @nestjs/swagger decorators to the shared DTO classes
- create a new Nx monorepo with
npx create-nx-workspace@latest - select the
angular-nestworkspace blueprint - install Swagger dependencies and bootstrap it according to the guide
- add input of type
Messageinapp.component.ts(doesn't make sense, just for testing):
export class AppComponent {
@Input() message: Message;
hello$ = this.http.get<Message>('/api/hello');
constructor(private http: HttpClient) {}
}
- decorate
messageproperty withApiProperty()inapi-interfaces.ts:
import { ApiProperty } from '@nestjs/swagger';
export class Message {
@ApiProperty()
message: string;
}
- run the NestJS app. It works fine, Swagger displays correct DTO structure in the Web view
- however, when I start the Angular app, I get errors:
WARNING in ./node_modules/@nestjs/common/utils/load-package.util.js 8:39-59
Critical dependency: the request of a dependency is an expression
WARNING in ./node_modules/express/lib/view.js 81:13-25
Critical dependency: the request of a dependency is an expression
WARNING in ./node_modules/@nestjs/mapped-types/dist/type-helpers.utils.js
Module not found: Error: Can't resolve 'class-transformer' in '.\node_modules\@nestjs\mapped-types\dist'
ERROR in ./node_modules/@nestjs/common/cache/cache.providers.js
Module not found: Error: Can't resolve 'cache-manager' in '.\node_modules\@nestjs\common\cache'
ERROR in ./node_modules/@nestjs/common/pipes/validation.pipe.js
Module not found: Error: Can't resolve 'class-transformer' in '.\node_modules\@nestjs\common\pipes'
...
Which looks like a bundling issue, similar to this one. I haven't found any elegant working solution to fix it in Angular build.
Approach 2. Use NestJS Swagger plugin and avoid decorators
This would be even a better way than the previous one, but it didn't work though. First, the Swagger plugin requires NestJS CLI, which is not the case in Nx monorepo. There's an issue suggesting some workarounds, but I haven't found them to be reliable enough and complete. Second, the plugin doesn't cover some important use cases, which still require decorators anyway. For instance:
@ApiProperty({
oneOf: [{ $ref: getSchemaPath(TypeA) }, { $ref: getSchemaPath(TypeB) }],
})
type: TypeA | TypeB;
And as described above, the decorators lead to errors.
Approach 3. Create interfaces out of DTO classes for dedicated use in the Angular app
What attracts me in this approach is the ability to abstract from the back-end specific logic, either Swagger, serialization, or validation decorators. We could do the following:
// Expose to the back-end
export class MessageDto {
@ApiProperty()
message: string;
}
// Expose to the front-end
export interface Message extends MessageDto {}
In this case, the Message interface gets compiled well with the Angular app. But as soon as we introduce more complicated DTO compositions (classes with nested DTOs), this approach doesn't work.
I would appreciate any other ideas.