Unable to save nested data in MongoDB using NestJs and Mongoose - node.js

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:
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?
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?

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();
}

use Address.city in postman, will definitely work

Related

How to use mongoose database Model as variable inside a class

What I'm trying to do is access the mongoose database Model (to perform operations in the database) inside a class as a static variable. I tried doing this, but when I run the server there's an error:
{"error":"TypeError: Device.db.find is not a function"}
This is the code from mongoosemodel.js:
import mongoose from 'mongoose';
const deviceSchema = new mongoose.Schema({
id: String,
name: String,
createdAt: Number,
updatedAt: Number
});
const DeviceModel = mongoose.model("Device", deviceSchema); //devices is the collection name
export default DeviceModel;
And on the class:
import mUUID from 'uuid-mongodb'
import mongoose from 'mongoose'
import database from '../mongoosemodel.js';
export default class Device {
static db = database
constructor (data = {}) {
const { id = mUUID.v4().toString(), name, createdAt, updatedAt } = data
this.id = id
this.name = name
this.createdAt = createdAt
this.updatedAt = updatedAt
}
static async getAll () {
const devices = await Device.db.find();
return devices.map(data => new Device(data))
}
static async findById (id) {
const devices = await Device.db.find();
const data = devices.find(d => d.id === id)
if (data) return new Device(data)
}
Maybe I'm doing something wrong with the imports/exports.

Nest JS - Client validation failed : Path is required

I'm posting here because I have been stuck on a problem for few hours now.
I am creating an API using Nest JS 8 and MongoDB, and I test it using Postman. When I want to execute a POST request (http://localhost:3000?nom=Antoine) to insert an object in my database, I have an error (500 : Internal server error) message that says "Client validation failed: nom: Path 'nom' is required (nom is the name of my object's property).
I've wandered every topic about this kind of issue, tried to upgrade my version of Nest, to use a middleware, to make sure the right version of every depedency was installed.
I don't want to remove the "required: true" property because i think it is necessary. I tried to set it to "false", which enabled me to insert the object in the database but without my property 'nom' (name in french).
If you guys have any help, here's my schema :
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
export type ClientDocument = Client & Document;
#Schema()
export class Client {
#Prop({ required: true })
nom: string;
}
export const ClientSchema = SchemaFactory.createForClass(Client);
And here is my controller :
import { Body, Controller, Delete, Get, Param, Post, Put} from '#nestjs/common';
import { ClientService } from './client.service';
import { ClientDto } from './dto/client.dto';
import { CreateClientDto } from './dto/create-client.dto';
import { UpdateClientDto } from './dto/update-client.dto';
#Controller('/client')
export class ClientController {
constructor(private readonly clientService: ClientService) {}
#Get()
async index(){
return await this.clientService.findAll();
}
#Get(':id')
async find(#Param('id') id: string) {
return await this.clientService.findOne(id);
}
#Post()
async create(#Body() createClientDto: CreateClientDto) {
console.log(createClientDto);
return await this.clientService.create(createClientDto);
}
#Put(':id')
async update(#Param('id') id: string, #Body() updateClientDto: ClientDto) {
return await this.clientService.update(id, updateClientDto);
}
#Delete(':id')
async delete(#Param('id') id: string) {
return await this.clientService.delete(id);
}
}
Thanks for looking
I found the solution (i still don't know why it works this way tho).
In my client.service.ts, i updated the create function from this :
async create(createClientDto: CreateClientDto): Promise<Client> {
return await new this.model({createClientDto}).save();
}
To this
async create(createClientDto: CreateClientDto): Promise<Client> {
return await new this.model({
...createClientDto,
createdAt: new Date(),
}).save();
}
Thanks for taking the time to answer, I hope this will help

MongooseError - Operation buffering timed out after 10000ms

I have following code for model.
import { DatabaseServer } from './database-server';
import { ProjectGroup } from './project-group';
import { ProjectUser } from './project-user';
import { prop, getModelForClass, ReturnModelType, modelOptions } from '#typegoose/typegoose';
import { defaultTransform, ModelBase } from '../general/model-base';
import { ObjectID } from 'mongodb';
import { Connection } from 'mongoose';
import { BeAnObject } from '#typegoose/typegoose/lib/types';
export class Datasource extends ModelBase {
#prop()
databaseServer?: DatabaseServer;
#prop()
databaseServerId?: ObjectID;
#prop()
datasource?: Datasource[];
#prop()
name?: string;
#prop()
projectGroups?: ProjectGroup[];
#prop()
projectUsers?: ProjectUser[];
}
const DatasourceModel = (
connection: Connection,
): ReturnModelType<typeof Datasource, BeAnObject> => {
return getModelForClass(Datasource, {
...defaultTransform,
...{
existingConnection: connection,
},
});
};
export { DatasourceModel };
And I am using above model as following.
await DatasourceModel(await this.masterContext).find({})
Where mastercontext is defined as below.
import {
Connection,
createConnection
} from 'mongoose';
export class MasterContext {
get context(): Promise<Connection> {
if (!this.m_context) {
this.m_context = createConnection('mongodb://localhost/Master', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
}
return this.m_context;
}
private m_context: Promise<Connection>;
}
I am getting error as following.
Operation datasources.find() buffering timed out after 10000m
If I change class name from export class Datasource to any other name (e.g. export class Datumsource) then error is not throwing.
So is Datasource reserved keyword in MongoDb or Mongoose or Typegoose?
from what i know, this error means that the connection is not connected, so the command (find) has an timeout
i also would recommend to either cache the DatasourceModel or only run the function once (the connection does not need to be connected to create an model, it only needs to be connected to do commands (like find))
so if you have an global connection, you should simply remove the function and just run getModelForClass, but if you have an "local" connection (like from an class property), then you should cache it there, example:
// i think this way of defining stuff is common in nestjs?
class Dummy {
public connection: mongoose.Connection;
public model: mongoose.Model;
constructor(connection: mongoose.Connection) {
this.connection = connection;
this.model = getModelForClass({ ...otherGlobalStuff, existingConnection: connection });
// or when wanting to use your function
this.model = DatasourceModel(connection);
}
}
// and if for one file only
let model = DatasourceModel(connection);
Some other notes:
if you want to use arrays in typegoose, you need to manually define the type with the type option, look here on why
only the mongoose.Types.ObjectId type should be used for an ObjectId

SequelizeDatabaseError: null value in column "name" violates not-null constraint (NodeJS + Sequelize + Postgres)

I'm trying to insert a student in my table 'students' but I have the following error:
Executing (default): INSERT INTO "students" ("id","created_at","updated_at") VALUES (DEFAULT,$1,$2) RETURNING *;
(node:6582) UnhandledPromiseRejectionWarning: SequelizeDatabaseError: null value in column "name" violates not-null constraint
I'm using Node.js + Sequelize (Postgres).
Here's my code:
Student.js (Model)
import Sequelize, { Model } from 'sequelize';
class Student extends Model {
static init(sequelize) {
super.init(
{
name: Sequelize.STRING,
email: Sequelize.STRING,
age: Sequelize.INTEGER,
weight: Sequelize.DECIMAL(10, 2),
height: Sequelize.DECIMAL(10, 2),
},
{
sequelize,
}
);
}
}
export default Student;
StudentController.js (Controller)
import Student from '../models/Student';
class StudentController {
async store(res, req) {
const student = await Student.create(req.body);
return res.json(student);
}
}
export default new StudentController();
routes.js (Routes)
import { Router } from 'express';
import StudentController from './app/controllers/StudentController';
const routes = new Router();
routes.post('/students', StudentController.store);
export default routes;
And I'm using Insomnia to send data via POST.
Any idea?
I can think of two thing that go wrong here
1. StudentController.js
class StudentController {
// Following should be `(req, res)`
async store(res, req) { // <-- it should be (req, res)
const student = await Student.create(req.body);
return res.json(student);
}
}
2. App setting
As you didn't share the code for initiating express app like following
const express = require("express")
const app = express()
You may have missed to use the body parser middleware which parses the json body.

How to use objectId validation in joiful?

I tried to joiful validation using mongodb objectId.but its throwing error Property 'ObjectId' does not exist on type 'typeof import("/home/lenovo/Music/basic/node_modules/joiful/index")'
import * as jf from "joiful";
import {ObjectId} from 'mongodb';
class SignUp {
#jf.string().required()
username?: string;
#jf
.string()
.required()
.min(8)
password?: string;
#jf.date()
dateOfBirth?: Date;
#jf.boolean().required()
subscribedToNewsletter?: boolean;
#jf.ObjectId().required()
id?:ObjectId;
}
const signUp = new SignUp();
signUp.username = "rick.sanchez";
signUp.password = "wubbalubbadubdub";
const { error } = jf.validate(signUp);
Is it possible to validate objectId using joiful.
I know that this question is along time ago, and the library maintainers didn't add this validator yet, for that I created a custom decorator that uses joiful custom method to make custom validation
import * as jf from 'joiful';
import Joi from 'joi';
import { ObjectId } from 'mongodb';
export const objectIdValidationDecorator = () => jf.any().custom(({ schema }: { schema: Joi.Schema }) => {
return schema.custom((value, helpers) => {
const objectId = new ObjectId(value);
if (objectId.equals(value)) {
return objectId;
} else {
return helpers.error('any.invalid');
}
});
})
Usage:
class MyObj {
#objectIdValidationDecorator().required()
referenceId:ObjectId
}

Resources