Search code examples
node.jsmongodbmongoosenestjsmongoose-schema

Unable to save nested data in MongoDB using NestJs and Mongoose


I am creating application where I am using NestJs framework and using MongoDB database with Mongoose ORM. I have nested data structure that I am trying to save inside database, but it's throwing error when I am saving it.

Below is my error:

[Nest] 11196  - 19/02/2022, 6:01:30 pm   ERROR [ExceptionsHandler] Cannot set property 'city' of undefined
TypeError: Cannot set property 'city' of undefined
at UserService.addUser (D:\nest\nested-schema-proj\src\user\user.service.ts:18:27)
at UserController.addUser (D:\nest\nested-schema-proj\src\user\user.controller.ts:11:33)

Below is my Postman request:

enter image description here

When I am posting data as raw JSON that can be seen in screenshot below then it is added successfully. Then why it not adding using first way?

enter image description here

Below is my code:

user.schema.ts

import mongoose from "mongoose";

const addressSchema = new mongoose.Schema({
  city:{type:String},
  state:{type:String}
});

export const UserSchema = new mongoose.Schema({

 name:{type:String},
 age:{type:Number},
 address:addressSchema

});


interface Address{
 city:string;
 state:string;
}

export interface AllUser{
 name:string;
 age:number;
 address:Address;
}

user.dto.ts

export class UserDto{
  name:string;
  age:number;
  address:Address
}

class Address{
  city:string;
  state:string;
}

user.controller.ts

import { Body, Controller, Post } from '@nestjs/common';
import { UserDto } from './dto/user.dto';
import { UserService } from './user.service';

@Controller()
export class UserController {
constructor(private userService:UserService){}

@Post('addUser')
addUser(@Body() userDto:UserDto){
    return this.userService.addUser(userDto);
  }
}

user.service.ts

import { Injectable } from '@nestjs/common';
import { InjectModel } from '@nestjs/mongoose';
import { Model} from 'mongoose';
import { UserDto } from './dto/user.dto';
import { AllUser } from './schema/user.schema';

@Injectable()
export class UserService {
constructor(@InjectModel('AllUser') private readonly userModel:Model<AllUser>){}

async addUser(userDto:UserDto): Promise<AllUser>{

    console.log(userDto);

     const data = new this.userModel();
     data.name = userDto.name;
     data.age = userDto.age;
     data.address.city = userDto.address.city; //Showing error in this line
     data.address.state = userDto.address.state;

     const result = await data.save();
     return result;

     //Posting data as a raw JSON as shown in 2nd screenshot
     // const data = new this.userModel(userDto);
     // const result = await data.save();
     // return result;  
   }
 }

What am I doing wrong?


Solution

  • You cannot save because the user model is interface object which is injected on initialisation of your service. Also you cannot this model by initiating and access its property.

    Instead you can expand the DTO and save it. Also you can manipulate you extra fields if you want from your code. In below example I have added date/time of document creation

     return await new this.userModel({
              ...userDto,
              created_at: moment.utc() //Manipulate your extra fields and set here
            }).save();
    

    Also you can set you own DTO object if you need to again maipulate data on controller level and pass that DTO direclty to service and just save it

    For example:

    //In Controller
     const data = new UserDto();
     data.name = userDto.name;
     data.age = userDto.age;
     data.address.city = userDto.address.city; 
     data.address.state = userDto.address.state;
     data.created_at: new Date();
    return await this.userService.addUser(data);
    
    //Service:
    async addUser(userDto:UserDto): Promise<AllUser>{
    
    console.log(userDto);
    
     return await new this.userModel({
          ...userDto,
          created_at: moment.utc() //Manipulate your extra fields and set here
        }).save();
    }