MongooseError - Operation buffering timed out after 10000ms - node.js

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

Related

How to initialize a TypeORM Repository based on a Generic Type "T" using getRepository()?

db.ts initializes TypeORM:
import { DataSource } from "typeorm"
import { Student, Teacher } from "core"
export class Database {
public static AppDataSource: DataSource;
static init () {
try {
Database.AppDataSource = new DataSource({
type: "mysql",
host: process.env.MYSQL_HOST || config.get('DBHost'),
port: Number(process.env.MYSQL_PORT || config.get('Port')),
username: process.env.MYSQL_USER || config.get('Username'),
password: process.env.MYSQL_PASSWORD || config.get('Password'),
database: process.env.MYSQL_DB || config.get('Database'),
entities: [Student, Teacher],
synchronize: true,
logging: false,
})
// to initialize initial connection with the database, register all entities
// and "synchronize" database schema, call "initialize()" method of a newly created database
// once in your application bootstrap
Database.AppDataSource.initialize()
.then(() => {
// here you can start to work with your database
console.log("TypeORM initialized successfully!")
})
.catch((error) => console.error(`TypeORM initialization failed! ${error}`))
} catch (error) {
console.error('[mysql.connector][init][Error]: ', error);
throw new Error('failed to initialized pool');
}
}
}
In RepositoryBase.ts:
import { IRepository, EntityBase } from "core"
import { Database } from "../../db"
export abstract class RepositoryBase<T extends EntityBase> implements IRepository<T> {
protected _repository;
constructor() {
Database.AppDataSource.getRepository(T); // XXX
}
}
Error on line marked XXX: 'T' only refers to a type, but is being used as a value here.ts(2693)
I have checked out How to initialize a TypeORM Repository based on a Generic Type "T"? but it uses Connection which doesn't apply here.
export abstract class RepositoryBase<T extends EntityBase> implements IRepository<T> {
protected _repository;
constructor(entity: EntityTarget<T>) {
this._repository = Database.AppDataSource.getRepository(entity);
}
export class UserRepository<Student extends EntityBase> extends RepositoryBase<Student> {
constructor() {
super(Student);
}
}

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

my typegoose save function didn't return needed type (NodeJs, typescript, graphql)

My function save of an object (in model.ts file) which is created by typegoose should return Promise<Todo> but it returns Promise<Document<any, {}>>
and i have this error :
Type 'Document<any, {}>' is missing the following properties from type 'Todo': createdAt, updatedAt, content, isDone
How should i correct this ?
model ts
import { getModelForClass } from "#typegoose/typegoose";
import { ObjectId } from "mongodb";
import { Todo } from "../../entities";
import { NewTodoInput } from "./input";
// This generates the mongoose model for us
export const TodoMongooseModel = getModelForClass(Todo);
export default class TodoModel {
async getById(_id: ObjectId): Promise<Todo | null> {
// Use mongoose as usual
return TodoMongooseModel.findById(_id).lean().exec();
}
async create(data: NewTodoInput): Promise<Todo> {
const todo = new TodoMongooseModel(data);
return todo.save();
}
}
input ts
import { Field, InputType, ID } from "type-graphql";
import { MaxLength, MinLength } from "class-validator";
#InputType()
export class NewTodoInput {
#Field()
#MaxLength(300)
#MinLength(1)
content: string | undefined;
}
entities todo ts
import { ObjectType, Field } from "type-graphql";
import { prop } from "#typegoose/typegoose";
import { ObjectId } from "mongodb";
#ObjectType()
export class Todo {
#Field()
readonly _id!: ObjectId;
#prop()
#Field(() => Date)
createdAt!: Date;
#prop()
#Field(() => Date)
updatedAt!: Date;
#prop()
#Field()
content!: string;
#prop({ default: false })
#Field()
isDone!: boolean;
}
Thank you.
with the unofficial mongoose types, it is an known problem that await document.save() does not return the correct typings
-> this is not an problem with typegoose, it is with #types/mongoose
the workaround is to use:
await doc.save();
return doc;
(or maybe in your case directly use the mongoose provided function .create)
PS: this is not a problem in the official types of mongoose (can be currently used with typegoose 8.0.0-beta.x)

Attach user id from access token using DTO

I create POST endpoint to create a new entity.
I also created schema for mongoose with field userId (to connect this entity to specified user) and DTO which I use on my POST method.
#UseGuards(JwtAuthGuard)
#Post("/")
createAction(#Request() req, #Body() createActionDto: CreateActionDto) {
return this.actionService.createAction(req?.user?.userId, createActionDto);
}
DTO:
import { IsString, IsNumber, IsUrl } from 'class-validator';
export class CreateActionDto {
userId: string;
#IsString()
name: string;
#IsNumber()
timeStart: number;
}
Schema:
import { Prop, Schema, SchemaFactory } from '#nestjs/mongoose';
import { Document } from 'mongoose';
#Schema()
export class Action extends Document {
#Prop()
userId: string;
#Prop()
name: string;
#Prop()
timeStart: number;
}
export const ActionSchema = SchemaFactory.createForClass(Action)
In the req property I have userId. What is the best way to create an entity and attach userId extracted from token?
Should I pass req to the service, and in the service set userId property on DTO like this?:
#Injectable()
export class ActionService {
constructor(
#InjectModel(Action.name) private actionModel: Model<Action>,
) { }
async createAction(req: string, createActionDto: CreateActionDto) {
createActionDto.userId = req.user.userId
// ... save to mongoose createActionDto
}
}
Is it a correct solution or there is another, a better way to deal with it?
Personally I would set the userId in the controller in order to not having to pass it around:
#UseGuards(JwtAuthGuard)
#Post("/")
createAction(#Request() req, #Body() createActionDto: CreateActionDto) {
createActionDto.userId = req?.user?.userId;
return this.actionService.createAction(createActionDto);
}
If you have many different controllers and DTOs that require the userId you could also define an Interceptor and do it there in order to reduce duplication:
#Injectable()
export class SetUserIdInterceptor implements NestInterceptor {
public intercept(_context: ExecutionContext, $next: CallHandler): Observable<any> {
const request: any = _context.switchToHttp().getRequest(); //instead of any you could also define a super-class for all DTOs that require the `userId`-property
request.body?.userId = req?.user?.userId;
return $next;
}
}
You can then use this interceptor on your route as follows:
#UseGuards(JwtAuthGuard)
#Post("/")
#UseInterceptors(SetUserIdInterceptor)
createAction(#Body() createActionDto: CreateActionDto) {
return this.actionService.createAction(createActionDto)
}

NodeJS, TypeScript, Mongoose unable to get document with FindById

Well, newbie with NodeJS and Mongoose, will try to get a document from a collection and use a class to manage results as i want to do.
I followed many tutos, but... i don't understand why, with the following code, i always get a null object with a findById() method on a model.
After hours spent, i decide to get help...
So, first i define a Model (simplified version) :
import { Schema, Model, model } from 'mongoose';
import { DocumentInterface } from './../interfaces/document-product-interface';
import { ProductClass } from './product-class';
var productSchema: Schema = new Schema(
{
_id: {
type: String,
required: 'Required _id'
},
id: {
type: String,
required: 'EAN required'
},
product_name: {
type: String
}
}
);
// Scheme methods
productSchema.method('title', ProductClass.prototype.title);
export const Products: Model<DocumentInterface> = model<DocumentInterface>('ProductClass', productSchema);
Next, create a class (for business purpose) :
import { ProductInterface } from './../interfaces/product-interface';
export class ProductClass implements ProductInterface{
public _id: String;
public id: String;
public product_name_fr?: string;
public product_name?: string;
public constructor() {}
public title(): string {
return 'something';
}
}
Next... The Document interface :
import { Document } from 'mongoose';
import { ProductInterface } from './product-interface';
import { ProductClass } from 'models/product-class';
export interface DocumentInterface extends ProductInterface, Document, ProductClass {}
Finally, just a controller, to get a product :
import { Products } from '../models/product-model';
import { SoappliProductInterface } from './../interfaces/soappli-product-interface';
import { Request, Response, NextFunction} from 'express';
export class ProductController {
public get(request: Request, response: Response, next: NextFunction) {
console.log('Search for a product : ' + request.params.ean);
Products.findById(request.params.ean, (error: any, product: DocumentInterface) => {
if (error) {
response.status(500).send( {message: error})
} else {
console.log('Product: ' + JSON.stringify(product));
if (product) {
console.log('Product title: ' + product.title);
response.status(200).send(product);
} else {
response.status(404).send({message: 'no matches for : ' + request.params.ean})
}
}
});
}
}
When i run with Postman and use a correct _id, got null for product...
If i remove all classes and interfaces, get the entire document...
What's wrong with this code ?
Thx

Resources