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
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:
DataType
functions that are ES6 class constructors. UPDATE: See https://github.com/strongloop/loopback-datasource-juggler/pull/1670 for a work that's already in progress.Either way, I think this is best discussed on GitHub. See https://github.com/strongloop/loopback-next/issues/2130