Typegoose enum arrayProp returns an error: Type is not a constructor - node.js

I have got a problem with the definition of my array schema. So basically, what I wanted to achieve is a single user Model with a property called games, that holds an array of games user is playing. The problem is I have got defined games as enum:
module Constants {
export enum Games {
LOL = 'League Of Legends',
}
}
export {Constants};
And now, when I try to attach it to the schema model like that:
#arrayProp({ required: true, items: Constants.Games })
games: Constants.Games[];
I receive an error (after a successful compilation, just before the server start)
^ const instance = new Type();
TypeError: Type is not a constructor
at baseProp (C:\Users\Borys\Desktop\lft\backend\node_modules\typegoose\lib\prop.js:114:22)
at C:\Users\Borys\Desktop\lft\backend\node_modules\typegoose\lib\prop.js:177:9
at DecorateProperty (C:\Users\Borys\Desktop\lft\backend\node_modules\reflect-metadata\Reflect.js:553:33)
at Object.decorate (C:\Users\Borys\Desktop\lft\backend\node_modules\reflect-metadata\Reflect.js:123:24)
at __decorate (C:\Users\Borys\Desktop\lft\backend\build\dataModel\User.js:4:92)
at Object.<anonymous> (C:\Users\Borys\Desktop\lft\backend\build\dataModel\User.js:64:1)
I have read a little bit about this error, but it relates to the required option for items/itemsRef I tried removing required, using enum, using even itemsRef and relating to the different set of documents but none of these worked for me.
Anyone could help/relate?

The problem is, you cannot use enums as an runtime mongoose type, so i would recommend using
#prop({ required: true, type: String, enum: Constants.Games })
games: Constants.Games[];
type for setting the type (which is string) (this option can be omitted - thanks to reflection)
enum for setting an enum to validate against

#arrayProp({ required: true, items: String })
games: Constants.Games[];
is a solution to this problem.
I would appreciate it if anyone could clarify and tell me more about why shouldn't I use enum in the items property.

Related

How to define a constant in the swagger definition of a NestJS Class?

I have a bunch of Exceptions that use the same interface, so it is easier for the frontend to distinct between them from the same endpoint.
I want to be able to document in swagger that MyWeirdExpection has a property type that has the value my-weird-exception (it comes from an string ENUM if that is relevant).
Is this possible to do?
Something like:
enum MyEnum {
MyWeirdExpection: 'my-weird-exception',
RegularExpection: 'my-regular-expection'
... more
}
#ApiProperty({
description: 'Frontend can react on this property',
type: String, // <-----------WHAT HERE?
})
type: MyEnum.MyWeirdExpection;
Again, my goal is that in the swagger definition, says that MyWeirdExpection.type is the constant 'my-weird-exception'
The following will provide a type of MyEnum.MyWeirdExpection with the value populated as 'my-weird-exception':
#ApiProperty({
description: "Search by asset type",
required: false,
default: MyEnum.MyWeirdExpection,
type: MyEnum.MyWeirdExpection
})
assetType4: string | undefined;

Models ref each other error: circular dependencies problem

admin.model.ts
import mongoose, { Schema, Document } from 'mongoose';
import UserRole, { IUserRole } from './user-role.model';
export interface IAdmin extends Document {
role: IUserRole;
}
let adminSchema = new Schema<IAdmin>({
role: {
type: Schema.Types.ObjectId, ref: UserRole
}
});
export default mongoose.model<IAdmin>('Admin', adminSchema);
user-role.model.ts
import { Schema, Document, model } from 'mongoose';
export interface IUserRole extends Document{
updated_by: IAdmin|string;
}
let userRoleSchema = new Schema<IUserRole>({
updated_by: {
type: Schema.Types.ObjectId, ref: Admin
}
})
export default model<IUserRole>('UserRole', userRoleSchema);
MongooseError: Invalid ref at path "updated_by". Got undefined
at validateRef (/home/ess24/ess-smartlotto/node-rest/node_modules/mongoose/lib/helpers/populate/validateRef.js:17:9)
at Schema.path (/home/ess24/ess-smartlotto/node-rest/node_modules/mongoose/lib/schema.js:655:5)
at Schema.add (/home/ess24/ess-smartlotto/node-rest/node_modules/mongoose/lib/schema.js:535:14)
at require (internal/modules/cjs/helpers.js:88:18)
[ERROR] 22:25:54 MongooseError: Invalid ref at path "updated_by". Got undefined
Here is my two model how can I solve this type of problem and how to deal with circular dependencies?
You have to put the ref values for UserRole and Admin in '' like this:
const adminSchema = new Schema<IAdmin>({
role: {
type: Schema.Types.ObjectId, ref: 'UserRole' // Name of the model you are referencing
}
});
let userRoleSchema = new Schema<IUserRole>({
updated_by: {
type: Schema.Types.ObjectId, ref: 'Admin' // <- and here
}
})
While the above answer by #gabriel-lupu is correct, in newer versions it seems another fix is recommended.
Fix:
adding quotes: ref: 'UserRole' // Recommended first fix
adding an arrow function: ref: () => UserRole
Typegoose documentation says the following:
Options ref and type can both also be defined without () =>, but is generally recommended to be used with.
If () => is not used, there can be problems when the class (/ variable) is defined after the decorator that requires it. Section in documentation for reference.
Because of the order classes are loaded and reordered at runtime, this might result in some references being null / undefined / not existing. This is why Typegoose provides the following:
class Nested {
#prop()
public someNestedProperty: string;
}
// Recommended first fix:
class Main {
#prop({ ref: () => Nested }) // since 7.1 arrow functions can be used to
defer getting the type
public nested: Ref<Nested>;
}
// Not recommended workaround (hardcoding model name):
class Main {
#prop({ ref: 'Nested' }) // since 7.0 it is recommended to use "console.log(getName(Class))" to get the generated name once and hardcode it like shown here
public nested: Ref<Nested>;
}
Section of documentation with the above example
Explanation:
The MongooseError: Invalid ref at path "updated_by". Got undefined error seems to be due to a circular dependency.
IAdmin has a role property that references IUserRole.
IUserRole has an updated_by property that references IAdmin.
When admin.model.ts loads it imports user-role.model.ts
user-role.model.ts needs IAdmin for the updated_by property from admin.model.ts
admin.model.ts hasn't finished loading yet resulting in updated_by which references it being undefined.
Relevant explanation from Mozilla about how modules deal with circular dependencies.
In a cyclic dependency, you end up having a loop in the graph.
Usually, this is a long loop. But to explain the problem, I’m going to
use a contrived example with a short loop.
A complex module graph with a 4 module cycle on the left. A simple 2
module cycle on the right.
Let’s look at how this would work with CommonJS modules. First, the
main module would execute up to the require statement. Then it would
go to load the counter module.
A commonJS module, with a variable being exported from main.js after a
require statement to counter.js, which depends on that import
The counter module would then try to access message from the export
object. But since this hasn’t been evaluated in the main module yet,
this will return undefined. The JS engine will allocate space in
memory for the local variable and set the value to undefined.
The message variable will be initialized and added to memory. But
since there’s no connection between the two, it will stay undefined in
the required module.
If the export were handled using live bindings, the counter module would see the correct value eventually. By the time the timeout runs, main.js’s evaluation would have completed and filled in the value.
how module imports work
Both? fixes seem to create a live binding.
Closures can close over imported values as well, which are regarded as live bindings, because when the original value changes, the imported one changes accordingly.
// closureCreator.js
import { x } from "./myModule.js";
export const getX = () => x; // Close over an imported live binding

nestjs- how to define multiple types for a field in mongodb mongoose with class properties in nest

take a look in this code:
#prop({
required: true,
autopopulate: true,
ref: Fund || Project,
})
organization: Ref<Fund> | Ref<Project>;
the property "organization" type can be Fund or Project.
in this example when the type is "Fund" i get the object. but when the type is "Project" i get null.
I dont know nest so much and I am starter in nest. I started using it very recently.
how can i solve that problem??
thanks.
You need two field to achieve this, first is the reference on what model the field refers to and the second is the objectid you stored to.
Note: on the name at enum,
Class Person {
#prop({required: true, enum: 'Fund' | 'Project'})
which: string;
#prop({refPath: "which"})
organization: Ref<Fund | Project>;
}
For reference just check this mongoose official docs

Type 'ObjectId' does not satisfy the constraint 'MongooseDocument'

I 'm trying to bind my Model with a mongoose schema using Typescript. Everything looks good except when I use ObjectId inside mongoose.Types.DocumentArray<>.
export interface TestModel extends mongoose.Document {
name: string;
sort: mongoose.Types.DocumentArray<mongoose.Schema.Types.ObjectId>;
}
const TestSchema = new mongoose.Schema({
name: String,
sort: [mongoose.Schema.Types.ObjectId]
});
export default mongoose.model<TestModel>('Test', TestSchema);
Following is the error I see
Type 'ObjectId' does not satisfy the constraint 'MongooseDocument'.
Type 'ObjectId' is missing the following properties from type
'MongooseDocument': $isDefault, $session, depopulate, equals, and 24
more.ts(2344)
If I use
sort: [mongoose.Schema.Types.ObjectId];
instead of
sort: mongoose.Types.DocumentArray<mongoose.Schema.Types.ObjectId>;
Everything looks good on schema side but I see mongoose type errors with methods like .pull() when doing operations on the modal results (TS is not picking it up as mongoose array type.)
Scratching my head with this for hours, any help will be really appreciated.
Found the problem. I'm stupid, it's a silly mistake I was using wrong Type in type definition. Should be using mongoose.Types.Array instead of mongoose.Types.DocumentArray

Mongoose Model Options field

Hi I just started playing with Mongoose. It seems pretty awesome!
Now coming from a Django background, how would one implement a type of options field like:
STATUS_OPTIONS : [{"Open",1},{"Closed",2},{"Pending",3"}]
status: { type:String, required:true, options:STATUS_OPTIONS },
So that it can be set like status = Open or something like that.
Or should this just be a normal String field and I set it accordingly in my app?
You can constrain a Mongoose schema string field to a set of enumeration values with the enum attribute:
var s = new Schema({
status: { type: String, enum: ['Open', 'Closed', 'Pending'] }
});
What you may be trying to do is reference some possibilities, right? Probably like an enum field type.
Well, you may have better luck using directly an String or using another Schema (but if you only need the strings Closed, Open, Pending, this wouldn't be needed).

Resources