Search code examples
typescriptloopbackjsloopback

Exception in deserialization of nested objects for datamodels in loopback 4


In a very small loopback4 project, we tried to generate nested entities like this:

import {Entity, model, property} from '@loopback/repository';

@model()
export class MySubEntity extends Entity {
  @property({
    type: 'string',
  })
  mySubEntityName?: string;

  constructor(data?: Partial<MySubEntity>) {
    super(data);
  }
}

@model()
export class MyMainEntity extends Entity {
  @property({
    type: 'string',
    id: true,
  })
  id?: string;

  @property({
    type: MySubEntity,
  })
  subEntity?: MySubEntity;

  constructor(data?: Partial<MyMainEntity>) {
    super(data);
  }
}

to create the following structure (also within the document db):

{
  "id": "uuid123213",
  "subEntity": {
    "mySubEntityName": "Hello test"
  }
}

with this controller:

  @post('/myMainEntity', {
    responses: {
      '200': {
        content: { 'application/json': { 'x-ts-type': MyMainEntity } },
      },
    },
  })
  async create(@requestBody() myMainEntity: MyMainEntity): Promise<MyMainEntity> {
    return await this.myMainEntityRepository.create(myMainEntity);
  }

Loopback4 compiles and starts the application. Unfortunately we get the following excepction when we try to create this object:

Unhandled error in POST /myMainEntity: 500 TypeError: Class constructor MySubEntity cannot be invoked without 'new'
    at MyMainEntity.set [as subEntity] (C:\projectOne\myMainEntity-ms\node_modules\loopback-datasource-juggler\lib\model-builder.js:598:81)
    at MyMainEntity.ModelBaseClass._initProperties (C:\projectOne\myMainEntity-ms\node_modules\loopback-datasource-juggler\lib\model.js:204:17)
    at MyMainEntity.ModelBaseClass (C:\projectOne\myMainEntity-ms\node_modules\loopback-datasource-juggler\lib\model.js:62:8)
    at new MyMainEntity (eval at createModelClassCtor (C:\projectOne\myMainEntity-ms\node_modules\loopback-datasource-juggler\lib\model-builder.js:671:21), <anonymous>:12:24)
    at Function.DataAccessObject.create (C:\projectOne\myMainEntity-ms\node_modules\loopback-datasource-juggler\lib\dao.js:331:13)
    at MyMainEntitysDataSource.onConnected (C:\projectOne\myMainEntity-ms\node_modules\loopback-datasource-juggler\lib\datasource.js:2524:14)
    at Object.onceWrapper (events.js:313:30)
    at emitNone (events.js:106:13)
    at MyMainEntitysDataSource.emit (events.js:208:7)
    at C:\projectOne\myMainEntity-ms\node_modules\loopback-datasource-juggler\lib\datasource.js:323:12
    at C:\projectOne\myMainEntity-ms\node_modules\loopback-connector-mongodb\lib\mongodb.js:310:25
    at parseHandler (C:\projectOne\myMainEntity-ms\node_modules\mongodb\lib\url_parser.js:134:38)
    at module.exports (C:\projectOne\myMainEntity-ms\node_modules\mongodb\lib\url_parser.js:25:12)
    at C:\projectOne\myMainEntity-ms\node_modules\loopback-connector-mongodb\lib\mongodb.js:305:16
    at result (C:\projectOne\myMainEntity-ms\node_modules\mongodb\lib\utils.js:414:17)
    at executeCallback (C:\projectOne\myMainEntity-ms\node_modules\mongodb\lib\utils.js:406:9)

Does anybody know what I am doing wrong here? Is there a different possibility to get a nested document structure which is typed?

I added the 'MySubEntity' after scaffolding the controller and repository (default crud)

Thank you


Solution

  • This looks like a limitation of the current implementation in loopback-datasource-juggler and/or the way how LoopBack 4 translates LB4 model definitions to Juggler models.

    The error is triggered by this code:

              // Assume the type constructor handles Constructor() call
              // If not, we should call new DataType(value).valueOf();
              this.__data[propertyName] = (value instanceof DataType) ? value : DataType(value);
    

    Juggler assumes that types (models) are defined as ES5 functions which can be called either with or without new. However, LB4 models are implemented as ES6 classes, which must be always constructed using new keyword.

    I think there are two options, they are not mutually exclusive:

    Either way, I think this is best discussed on GitHub. See https://github.com/strongloop/loopback-next/issues/2130