Reputation: 1492
I am working on an API with NestJS, and because I have DTO's I am using an AutoMapper (made by @nartc and/or nestjsx), I have tried to make my example as small as I could with the Foo
example, because I use multiple files.
This is my module:
// foo.module.ts
import { Module } from "@nestjs/common";
import { MongooseModule } from "@nestjs/mongoose";
import { Foo, FooSchema } from "./foo.entity.ts";
import { FooController } from "./foo.controller.ts";
import { FooService } from "./foo.service.ts";
import { FooProfile } from "./foo.profile.ts";
@Module({
imports: [
MongooseModule.forFeature([
{
name: Foo.name,
schema: FooSchema,
collection: "foos",
}
])
// FooProfile <-- if I uncomment this, the program will give the error (shown at the bottom of this question)
],
controllers: [FooController],
providers: [FooProivder],
})
export class FooModule {}
This is my entity:
// foo.entity.ts
import { Schema, SchemaFactory, Prop } from "@nestjs/mongoose";
import { Document } from "mongoose";
@Schema()
export class Foo extends Document { // if I remove the `extends Document` it works just fine
@Prop({ required: true })
name: string;
@Prop()
age: number
}
export const FooSchema = SchemaFactory.createForClass(Foo);
This is my DTO:
// foo.dto.ts
export class FooDTO {
name: string;
}
This is my controller:
// foo.controller.ts
import { Controller, Get } from "@nestjs/common";
import { InjectMapper, AutoMapper } from "nestjsx-automapper";
import { Foo } from "./foo.entity";
import { FooService } from "./foo.service";
import { FooDTO } from "./dto/foo.dto";
@Controller("foos")
export class FooController {
constructor(
private readonly fooService: FooService
@InjectMapper() private readonly mapper: AutoMapper
) {}
@Get()
async findAll() {
const foos = await this.fooService.findAll();
const mappedFoos = this.mapper.mapArray(foos, Foo, FooDTO);
// ^^ this throws an error of the profile being undefined (duh)
return mappedFoos;
}
}
This is my profile:
// foo.profile.ts
import { Profile, ProfileBase, InjectMapper, AutoMapper } from "nestjsx-automapper";
import { Foo } from "./foo.entity";
import { FooDTO } from "./foo.dto";
@Profile()
export class FooProfile extends ProfileBase {
constructor(@InjectMapper() private readonly mapper: AutoMapper) {
// I've read somewhere that the `@InjectMapper() private readonly` part isn't needed,
// but if I exclude that, it doesn't get the mapper instance. (mapper will be undefined)
super();
this.mapper.createMap(Foo, FooDTO);
}
}
If I uncomment the line I highlighted in the module, it will result in the following error..
[Nest] 11360 - 2020-08-18 15:53:06 [ExceptionHandler] Cannot read property 'plugin' of undefined +1ms
TypeError: Cannot read property 'plugin' of undefined
at Foo.Document.$__setSchema ($MYPATH\node_modules\mongoose\lib\document.js:2883:10)
at new Document ($MYPATH\node_modules\mongoose\lib\document.js:82:10)
at new Foo($MYPATH\dist\foo\foo.entity.js:15:17)
I have also referred to this answer on stackoverflow, but that doesn't work for me either. I have also combined that with the documentation, but with no luck.. How would I get the AutoMapper to register my profiles?
Update
The error seems to originate from the foo entity, if I remove the extends Document
and the Schema()
, Prop({ ... })
from the class it works fine, it seems like I have to inject mongoose or something?
Upvotes: 4
Views: 4153
Reputation: 1920
What worked for me.
1. Updated all the absolute paths for models, schemas, entities (is easy if you search for from '/src
in your projects, and update all the routes to relative paths)
from:
import { User } from 'src/app/auth/models/user/user.entity';
to:
import { User } from './../../auth/models/user/user.entity';
2. mongoose imports:
from:
import mongoose from 'mongoose';
to:
import * as mongoose from 'mongoose';
3. Remove validation pipe if you don't use it. For some reason (I think i don't use them on the controller, I didn't investigate, I've removed from one controller the Validation Pipe) so If you have this try it:
from:
@Controller('someroute')
@UsePipes(new ValidationPipe())
export class SomeController {}
to:
@Controller('someroute')
export class SomeController {}
I hope my solution worked for you ^_^
Upvotes: 1
Reputation: 5098
In your module, just import the path to the profile like below:
import 'relative/path/to/foo.profile';
By importing the path to file, TypeScript
will include the file in the bundle and then the @Profile()
decorator will be executed. When @Profile()
is executed, AutoMapperModule
keeps track of all the Profiles
then when it's turn for NestJS to initialize AutoMapperModule
(with withMapper()
method), AutoMapperModule
will automatically add the Profiles to the Mapper instance.
With that said, in your FooProfile
's constructor, you'll get AutoMapper
instance that this profile will be added to
@Profile()
export class FooProfile extends ProfileBase {
// this is the correct syntax. You would only need private/public access modifier
// if you're not going to use this.mapper outside of the constructor
// You DON'T need @InjectMapper() because that's Dependency Injection of NestJS.
// Profile isn't a part of NestJS's DI
constructor(mapper: AutoMapper) {
}
}
The above answer will solve your problems with AutoMapper
. As far as your Mongoose problem, I would need a sample repro to tell for sure. And also, visit our Discord for this kind of question.
Upvotes: 1