Nx: Problem with decorators

Created on 13 Jul 2020  路  8Comments  路  Source: nrwl/nx

Current Behavior

I have a nx workspace. This workspace contains of two projects a backend (nest.js) and a web-frontend (next.js) and a shared library. This library handles the http requests. In the library, I store as well the shared interfaces/DTO's like the following:

import { ApiProperty } from '@nestjs/swagger'

export class AccessTokenResponseDto {
    @ApiProperty({
        example: 'xxxxx.xxxxx.xxxxx',
    })
    public token!: string
}

As you can see I use the decorator @ApiProperty from the @nestjs/swagger package. This line is causing the bug.
Using the library on the backend side is fine but importing it on frontend side breaks the website and I receive the following error:

SyntaxError: {PROJECT}/libs/rest-client/src/dto/auth/access-token-response.dto.ts: Support for the experimental syntax 'decorators-legacy' isn't currently enabled (4:5):

After some research I changed my .babelrc.json to babel.config.json. It looks like this change fixed the error above but a new one appeared.

{PROJECT}/node_modules/@nestjs/common/cache/cache.providers.js Module not found: Can't resolve 'cache-manager' in '{PROJECT}/node_modules/@nestjs/common/cache'

I also tried multiple times to reach somebody in slack but I looks like this is a more non-trival issue.

Expected Behavior

I expect that this works without compilation errors.

Steps to Reproduce

  1. Create a simple nx workspace
  2. Add a next.js project and a library.
  3. In this library use the @nestjs/swagger decorators
  4. Import this library in the next.js project

Failure Logs

contents of babel.config.json:

{
    "presets": [
        [
            "@babel/preset-env",
            {
                "useBuiltIns": "usage",
                "corejs": 3
            }
        ],
        "next/babel"],
    "plugins": [
        ["@babel/plugin-proposal-decorators", { "legacy": true }],
        ["@babel/plugin-proposal-class-properties", { "loose": true }]
    ]
}

Environment

>  NX  Report

  @nrwl/angular : Not Found
  @nrwl/cli : 9.4.5
  @nrwl/cypress : 9.4.5
  @nrwl/eslint-plugin-nx : 9.4.5
  @nrwl/express : Not Found
  @nrwl/jest : 9.4.5
  @nrwl/linter : 9.4.5
  @nrwl/nest : 9.4.5
  @nrwl/next : 9.4.5
  @nrwl/node : 9.4.5
  @nrwl/react : 9.4.5
  @nrwl/schematics : Not Found
  @nrwl/tao : 9.4.5
  @nrwl/web : 9.4.5
  @nrwl/workspace : 9.4.5
  typescript : 3.8.3

I maybe should add that I have not that much experience in webpack, babel and bundling so far, but I understand it's purpose.

react bug

All 8 comments

Facing similar kind of issue with having "Angular" as front-end and "Nest" as back-end due to swagger decorators in interface.

@FrozenPandaz can you please check this?

I did a bit of investigation.
To summarize it: Decorators are working, but using them is causing the error. Is there a way to remove decorators?

If you get this error

{PROJECT}/node_modules/@nestjs/common/cache/cache.providers.js Module not found: Can't resolve 'cache-manager' in '{PROJECT}/node_modules/@nestjs/common/cache'

then your decorators are working. There has been a misconception. The problem which I and probably @pradeeprajr93 have is that we're using the library on the frontend. The library uses decorators which are importing packages from the backend. (In general decorators are functions. How do decorators work.). I do not want to include these packages in the frontend bundle anyway.

So my second thought was to disable decorators when serving/building the frontend. I've tried several packages f.e.

Unfortunately none of them worked. Is there a way to disable/remove decorators ?

So, I don't know how I feel about this, but it is all using mechanisms provided to us. I'll try my best to break it down.

With the below setup, I can import my-object.ts from a library in both my Nest project and my Angular project. Nest compiles with the Swagger decorator, and my Angular app compiles after having the Swagger decorator replaced with an empty one. Unfortunately you'd have to do this with all of the decorators that are not cross-compatible. This should also go the other way around, using Angular decorators but replacing them with empty decorators for Nest compiles.

Would anyone have any alternative approaches?.. something feels off about this (maybe the scalability?..) but it is again using mechanisms given to us out-of-the-box - no hacks.

libs/api-interfaces/src/lib/my-object.ts

import { ApiProperty } from './decorators-nest';

export class MyObject {
  @ApiProperty()
  objectId: string;
}

libs/api-interfaces/src/lib/decorators-nest.ts

export { ApiProperty } from '@nestjs/swagger';

libs/api-interfaces/src/lib/decorators-angular.ts

import "reflect-metadata";

export function ApiProperty(...args): PropertyDecorator {
  return function () {
  }
}

angular.json

{
  "projects": {
    "my-frontend-angular-app": {
      "architect": {
        "build": {
          "builder": "@angular-devkit/build-angular:browser",
          "configurations": {
            "default": {
              "fileReplacements": [
                // REPLACE THE NEST DECORATOR WITH ANGULAR FRIENDLY
                {
                  "replace": "libs/api-interfaces/src/lib/decorators-nest.ts",
                  "with": "libs/api-interfaces/src/lib/decorators-angular.ts"
                }
              ]
            },
            "production": {
              "fileReplacements": [
                {
                  "replace": "apps/my-frontend-angular-app/src/environments/environment.ts",
                  "with": "apps/my-frontend-angular-app/src/environments/environment.prod.ts"
                },
                // REPLACE THE NEST DECORATOR WITH ANGULAR FRIENDLY
                {
                  "replace": "libs/api-interfaces/src/lib/decorators-nest.ts",
                  "with": "libs/api-interfaces/src/lib/decorators-angular.ts"
                }
              ]
            }
          }
        },
        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            // SPECIFY THE DEFAULT DEFINITION
            "browserTarget": "my-frontend-angular-app:build:default"
          },
          "configurations": {
            "production": {
              "browserTarget": "my-frontend-angular-app:build:production"
            }
          }
        }
      }
    }
  }
}

@METACEO
Thanks for the workaround. I wish it would work, unfortunately it didn't work for me.
It will be way more helpful if you share a stackblitz project (or similar) with your workaround.

Thanks

Hey @pradeeprajr93, sorry to hear, I don't know if I can prove this out via stackblitz but I should be able to throw together a quick repo with Nest+Swagger and Angular and the above configurations.

@pradeeprajr93 @ericgoe I've used the strategy I mentioned above (with more validation examples, too) within this example repo: https://github.com/METACEO/nx-demo-swagger-decorators

Note I have encountered this myself, and would like to figure out a workaround.

I posted this on an existing SO post

Was this page helpful?
0 / 5 - 0 ratings