Jacob Banks

Jacob Banks

1570154912

Typegoose - Define Mongoose models using TypeScript classes

Basic usage

import { prop, getModelForClass } from '@typegoose/typegoose';
import * as mongoose from 'mongoose';

class User {
  @prop()
  name?: string;
}

const UserModel = getModelForClass(User);

// UserModel is a regular Mongoose Model with correct types
(async () => {
  await mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true });

  const { _id: id } = await UserModel.create({ name: 'JohnDoe' });
  const user = await UserModel.findById(id).exec();

  console.log(user);
  // prints { _id: 59218f686409d670a97e53e0, name: 'JohnDoe', __v: 0 }
})();

Motivation

A common problem when using Mongoose with TypeScript is that you have to define both the Mongoose model and the TypeScript interface. If the model changes, you also have to keep the TypeScript interface file in sync or the TypeScript interface would not represent the real data structure of the model.

Typegoose aims to solve this problem by defining only a TypeScript interface (class) which need to be enhanced with special Typegoose decorators.

Under the hood it uses the Reflect & reflect-metadata API to retrieve the types of the properties, so redundancy can be significantly reduced.

Instead of:

interface Car {
  model?: string;
}

interface Job {
  title?: string;
  position?: string;
}

interface User {
  name?: string;
  age: number;
  job?: Job;
  car: Car | string;
}

mongoose.model('User', {
  name: String,
  age: { type: Number, required: true },
  job: {
    title: String;
    position: String;
  },
  car: { type: Schema.Types.ObjectId, ref: 'Car' }
});

mongoose.model('Car', {
  model: string,
});

You can just:

class Job {
  @prop()
  title?: string;

  @prop()
  position?: string;
}

class Car {
  @prop()
  model?: string;
}

class User {
  @prop()
  name?: string;

  @prop({ required: true })
  age!: number;

  @prop()
  job?: Job;

  @prop({ ref: Car })
  car?: Ref<Car>;
}

Please note that sub documents do not have to extend Typegoose. You can still give them default value in prop decorator, but you can’t create static or instance methods on them.


Requirements

  • TypeScript 3.2+
  • Node 8+
  • mongoose ^5.7.1
  • emitDecoratorMetadata and experimentalDecorators must be enabled in tsconfig.json

Install

npm i -s @typegoose/typegoose

You also need to install mongoose, since version 5 it is listed as a peer-dependency

npm i -s mongoose

Migrate to 6.0.0

Testing

npm test

Versioning

Major.Minor.Fix (or how npm expresses it Major.Minor.Patch)
(This Project should comply with Semver)

API Documentation

Please use the new guides and the new docs this here is just for “legacy reasons” and might be deleted later

Typegoose class

Since 6.0.0 deprecated, please try to remove it

###Methods

getModelForClass<T>(cl: T)

This method returns the corresponding Mongoose Model for the class (T). If no Mongoose model exists for this class yet, one will be created automatically (by calling the method setModelForClass).

setModelForClass<T>(cl: T)

This Method is Deprecated see Migrate to 6.0.0

Property decorators

Typegoose comes with TypeScript decorators, which responsibility is to connect the Mongoose schema behind the TypeScript class.

prop(options)

The prop decorator adds the target class property to the Mongoose schema as a property. Typegoose checks the decorated property’s type and sets the schema property accordingly. If another Typegoose extending class is given as the type, Typegoose will recognize this property as a sub document.

The options object accepts multiple config properties:

  • required: Just like the Mongoose required it accepts a handful of parameters. Please note that it’s the developer’s responsibility to make sure that if required is set to false then the class property should be optional.

    Note: for coding style (and type completion) you should use ! when it is marked as required

    // this is now required in the schema
    @prop({ required: true })
    firstName!: string;
    
    // by default, a property is not required
    @prop()
    lastName?: string; // using the ? optional property
    
  • index: Tells Mongoose whether to define an index for the property.

    @prop({ index: true })
    indexedField?: string;
    
  • unique: Just like the Mongoose unique, tells Mongoose to ensure a unique index is created for this path.

    // this field is now unique across the collection
    @prop({ unique: true })
    uniqueId?: string;
    
  • enum: The enum option accepts a string array. The class property which gets this decorator should have an enum-like type which values are from the provided string array. The way how the enum is created is delegated to the developer, Typegoose needs a string array which hold the enum values, and a TypeScript type which tells the possible values of the enum. However, if you use TS 2.4+, you can use string enum as well.

    enum Gender {
      MALE = 'male',
      FEMALE = 'female',
    }
    
    @prop({ enum: Gender })
    gender?: Gender;
    
  • lowercase: for strings only; whether to always call .toLowerCase() on the value.

    @prop({ lowercase: true })
    nickName?: string;
    
  • uppercase: for strings only; whether to always call .toUpperCase() on the value.

    @prop({ uppercase: true })
    nickName?: string;
    
  • trim: for strings only; whether to always call .trim() on the value.

    @prop({ trim: true })
    nickName?: string;
    
  • default: The provided value will be the default for that Mongoose property.

    @prop({ default: 'Nick' })
    nickName?: string;
    
  • _id: When false, no _id is added to the subdocument

    class Car {}
    
    @prop({ _id: false })
    car?: Car;
    
  • ref: By adding the ref option with another Typegoose class as value, a Mongoose reference property will be created. The type of the property on the Typegoose extending class should be Ref<T> (see Types section).

    class Car {}
    
    @prop({ ref: Car })
    car?: Ref<Car>;
    
  • refPath: Is the same as ref, only that it looks at the path specified, and this path decides which model to use

    class Car {}
    class Shop {}
    
    // in another class
    class Another {
      @prop({ required: true, enum: 'Car' | 'Shop' })
      which!: string;
    
      @prop({ refPath: 'which' })
      kind?: Ref<Car | Shop>;
    }
    
  • min / max (numeric validators): Same as Mongoose numberic validators.

    @prop({ min: 10, max: 21 })
    age?: number;
    
  • minlength / maxlength / match (string validators): Same as Mongoose string validators.

    @prop({ minlength: 5, maxlength: 10, match: /[0-9a-f]*/ })
    favouriteHexNumber?: string;
    
  • validate (custom validators): You can define your own validator function/regex using this. The function has to return a boolean or a Promise (async validation).

    // you have to get your own `isEmail` function, this is a placeholder
    
    @prop({ validate: (value) => isEmail(value)})
    email?: string;
    
    // or
    
    @prop({ validate: async (value) => { await isEmail(value) })
    email?: string;
    
    // or
    
    @prop({ validate: {
        validator: val => isEmail(val),
        message: `{VALUE} is not a valid email`
    }})
    email?: string;
    
    // or
    
    @prop({ validate: /\S+@\S+\.\S+/ })
    email?: string;
    
    // you can also use multiple validators in an array.
    
    @prop({ validate:
      [
        {
            validator: val => isEmail(val),
            message: `{VALUE} is not a valid email`
        },
        {
            validator: val => isBlacklisted(val),
            message: `{VALUE} is blacklisted`
        }
      ]
    })
    email?: string;
    
  • alias (alias): Same as Mongoose Alias, only difference is the extra property for type completion

    class Dummy {
      @prop({ alias: "helloWorld" })
      public hello: string; // will be included in the DB
      public helloWorld: string; // will NOT be included in the DB, just for type completion (gets passed as hello in the DB)
    }
    

Virtuals

  • Mongoose gives developers the option to create virtual properties. This means that actual database read/write will not occur these are just ‘calculated properties’. A virtual property can have a setter and a getter. TypeScript also has a similar feature which Typegoose uses for virtual property definitions (using the prop decorator).

    example:

    class Name {
      @prop()
      firstName?: string;
    
      @prop()
      lastName?: string;
    
      // this will create a virtual property called 'fullName'
      get fullName() {
        return `${this.firstName} ${this.lastName}`;
      }
      set fullName(full) {
        const [firstName, lastName] = full.split(' ');
        this.firstName = firstName;
        this.lastName = lastName;
      }
    }
    

    DB Document:

    {
      _id: ObjectId("<some long id>"),
      firstName: "Will",
      lastName: "Smith"
    }
    
  • Non-Virtuals are supported too:
    example:

    function setFullName(val: string[]): string {
      return val.join(' ');
    }
    
    function getFullname(val: string): string[] {
      return val.split(' ');
    }
    
    class Name {
      @prop({ set: setFullName, get: getFullname })
      fullname: string[]; // this is just for type completion & getting the type for the schema
    }
    
    ...
    await NameModel.create({ fullname: ['Will', 'Smith'] });
    const [first, last]: string[] = (await NameModel.findOne({}).exec()).fullname;
    

    DB Document:

    {
      _id: ObjectId("<some long id>"),
      fullname: "Will Smith"
    }
    
  • Virtual-Populate is also supported by doing

    class RefClass {
      @prop({ required: true, ref: Virtual })
      public refToName: Ref<Virtual>;
    }
    class Name {
      @prop({ ref: RefClass, foreignField: 'refToName', localField: '_id', justOne: false })
      public somevalue: Ref<RefClass>;
    }
    

Options (look here for more details):

  • ref: This is like a normal ref [Required]
  • foreignField: Which property(on the ref-Class) to match localField against [Required]
  • localField: Which property(on the current-Class) to match foreignField against [Required]
  • justOne: Return as One Document(true) or as Array(false) [Optional]
  • count: Return the number of Documents found instead of the actual Documents [Optional]

arrayProp(options)

The arrayProp is a prop decorator which makes it possible to create array schema properties.

The options object accepts required, enum and default, just like the prop decorator. In addition to these the following properties exactly one should be given:

  • items: This will tell Typegoose that this is an array which consists of primitives (if String, Number, or other primitive type is given) or this is an array which consists of subdocuments (if it’s extending the Typegoose class).

    @arrayProp({ items: String })
    languages?: string[];
    

Note that unfortunately the reflect-metadata API does not let us determine the type of the array, it only returns Array when the type of the property is queried. This is why redundancy is required here.

  • itemsRef: In mutual exclusion with items, this tells Typegoose that instead of a subdocument array, this is an array with references in it. On the Mongoose side this means that an array of Object IDs will be stored under this property. Just like with ref in the prop decorator, the type of this property should be Ref<T>[].

    class Car {}
    
    // in another class
    @arrayProp({ itemsRef: Car })
    previousCars?: Ref<Car>[];
    
  • itemsRefPath(IRP): Is the same as itemsRef only that it looks at the specified path of the class which specifies which model to use

    class Car {}
    class Shop {}
    
    // in another class
    class Another {
      @prop({ required: true, enum: ['Car', 'Shop'] })
      which!: string;
    
      @arrayProp({ itemsRefPath: 'which' })
      items?: Ref<Car | Shop>[];
    }
    

mapProp(options)

The mapProp is a prop decorator which makes it possible to create map schema properties.

The options object accepts enum and default, just like prop decorator. In addition to these the following properties are accepted:

  • of : This will tell Typegoose that the Map value consists of primitives (if String, Number, or other primitive type is given) or this is an array which consists of subdocuments (if it’s extending the Typegoose class).

    class Car {
      @mapProp({ of: Car })
      public keys?: Map<string, Car>;
    }
    

Method decorators

Method Decorators are deprecated see Migrate to 6.0.0

Class decorators

Mongoose allows the developer to add pre and post hooks / middlewares to the schema. With this it is possible to add document transformations and observations before or after validation, save and more.

Typegoose provides this functionality through TypeScript’s class decorators.

modelOptions

The Model Options can be used like below

@modelOptions({ existingMongoose, schemaOptions, existingConnection })
class Name {}

The Options for @modelOptions:

  • existingMongoose: mongoose: An existing Mongoose instance can also be passed down. If given, Typegoose uses this Mongoose instance’s model methods.
  • schemaOptions: mongoose.SchemaOptions: Additional schema options can be passed down to the schema-to-be-created.
  • existingConnection: mongoose.Connection: An existing Mongoose connection can also be passed down. If given, Typegoose uses this Mongoose instance’s model methods.

pre

We can simply attach a @pre decorator to the Typegoose class and define the hook function like you normally would in Mongoose. (Method supports REGEXP)

@pre<Car>('save', function(next) {
  if (this.model === 'Tesla') {
    this.isFast = true;
  }
  next();
})
class Car {
  @prop({ required: true })
  model!: string;

  @prop()
  isFast?: boolean;
}

This will execute the pre-save hook each time a Car document is saved. Inside the pre-hook Mongoose binds the actual document to this.

Note that additional typing information is required either by passing the class itself as a type parameter <Car> or explicity telling TypeScript that this is a Car (this: Car). This will grant typing informations inside the hook function.

post

Same as pre, the post hook is also implemented as a class decorator. Usage is equivalent with the one Mongoose provides. (Method supports REGEXP)

@post<Car>('save', (car) => {
  if (car.topSpeedInKmH > 300) {
    console.log(car.model, 'is fast!');
  }
})
class Car {
  @prop({ required: true })
  model!: string;

  @prop({ required: true })
  topSpeedInKmH!: number;
}

Of course this is not the document in a post hook (see Mongoose docs). Again typing information is required either by explicit parameter typing or by providing a template type.

plugin

Using the plugin decorator enables the developer to attach various Mongoose plugins to the schema. Just like the regular schema.plugin() call, the decorator accepts 1 or 2 parameters: the plugin itself, and an optional configuration object. Multiple plugin decorator can be used for a single Typegoose class.

If the plugin enhances the schema with additional properties or instance / static methods this typing information should be added manually to the Typegoose class as well.

import * as findOrCreate from 'mongoose-findorcreate';

@plugin(findOrCreate)
class User {
  // this isn't the complete method signature, just an example
  static findOrCreate(condition: DocumentType<User>):
    Promise<{ doc: DocumentType<User>, created: boolean }>;
}

const UserModel = getModelForClass(User);
const result = await UserModel.findOrCreate({ ... });

index

The @index decorator can be used to define advanced index types and index options not available via the index option of the @prop property decorator, such as compound indices, GeoJSON index types, partial indices, expiring documents, etc. Any values supported by MongoDB’s createIndex() are also valid for @index. For more info refer to interface IndexOptions

@index({ article: 1, user: 1 }, { unique: true })
@index({ location: '2dsphere' })
@index({ article: 1 }, { partialFilterExpression: { stars: { $gte: 4.5 } } })
class Location {
 @prop()
 article?: number;

 @prop()
 user?: number;

 @prop()
 stars?: number;

 @arrayProp({ items: Array })
 location?: [[Number]]
}

Types

Some additional types were added to make Typegoose more user friendly.
(for some additional types, that are not exported by default can be accessed via import * as types from 'typegoose/types')

DocumentType

This is basically the logical ‘and’ of the T and the mongoose.Document, so that both the Mongoose instance properties/functions and the user defined properties/instance methods are available on the instance.

ReturnModelType

This is the logical ‘and’ of mongoose.Model<DocumentType<T>> and T, so that the Mongoose model creates DocumentType<T> typed instances and all user defined static methods are available on the model.

Ref

For reference properties: Ref<T> - T if populated and ObjectID if unpopulated. -> there are TypeGuards for this to check named:

  • isDocument(T): returns true if T is populated, false otherwise
  • isDocumentArray(T): returns true if T is an Array AND is fully populated

Improvements

  • Add Tests for:
    • Hooks: add hook test for pre & post with error

Notes

  • mongoose is a peer-dependency, and a dev dependency to install it for dev purposes
  • Please dont add comments with +1 or something like that, use the Reactions
  • Typegoose cannot be used with classes of the same name, it will always return the first build class with that name
  • All Models in Typegoose are set to strict by default, and cant be changed!
  • npm run doc generates all documentation for all files that can be used as modules (is used for github-pages)
  • npm run doc:all generates documentation even for internal modules
  • This module should not be used with ts-node --transpile-only, --type-check must be added at least, szokodiakos#196

#typescript #mongodb

What is GEEK

Buddha Community

Typegoose - Define Mongoose models using TypeScript classes

Annex liu

1576485470

This works for me, thinks!

Jacob Banks

Jacob Banks

1570154912

Typegoose - Define Mongoose models using TypeScript classes

Basic usage

import { prop, getModelForClass } from '@typegoose/typegoose';
import * as mongoose from 'mongoose';

class User {
  @prop()
  name?: string;
}

const UserModel = getModelForClass(User);

// UserModel is a regular Mongoose Model with correct types
(async () => {
  await mongoose.connect('mongodb://localhost:27017/test', { useNewUrlParser: true });

  const { _id: id } = await UserModel.create({ name: 'JohnDoe' });
  const user = await UserModel.findById(id).exec();

  console.log(user);
  // prints { _id: 59218f686409d670a97e53e0, name: 'JohnDoe', __v: 0 }
})();

Motivation

A common problem when using Mongoose with TypeScript is that you have to define both the Mongoose model and the TypeScript interface. If the model changes, you also have to keep the TypeScript interface file in sync or the TypeScript interface would not represent the real data structure of the model.

Typegoose aims to solve this problem by defining only a TypeScript interface (class) which need to be enhanced with special Typegoose decorators.

Under the hood it uses the Reflect & reflect-metadata API to retrieve the types of the properties, so redundancy can be significantly reduced.

Instead of:

interface Car {
  model?: string;
}

interface Job {
  title?: string;
  position?: string;
}

interface User {
  name?: string;
  age: number;
  job?: Job;
  car: Car | string;
}

mongoose.model('User', {
  name: String,
  age: { type: Number, required: true },
  job: {
    title: String;
    position: String;
  },
  car: { type: Schema.Types.ObjectId, ref: 'Car' }
});

mongoose.model('Car', {
  model: string,
});

You can just:

class Job {
  @prop()
  title?: string;

  @prop()
  position?: string;
}

class Car {
  @prop()
  model?: string;
}

class User {
  @prop()
  name?: string;

  @prop({ required: true })
  age!: number;

  @prop()
  job?: Job;

  @prop({ ref: Car })
  car?: Ref<Car>;
}

Please note that sub documents do not have to extend Typegoose. You can still give them default value in prop decorator, but you can’t create static or instance methods on them.


Requirements

  • TypeScript 3.2+
  • Node 8+
  • mongoose ^5.7.1
  • emitDecoratorMetadata and experimentalDecorators must be enabled in tsconfig.json

Install

npm i -s @typegoose/typegoose

You also need to install mongoose, since version 5 it is listed as a peer-dependency

npm i -s mongoose

Migrate to 6.0.0

Testing

npm test

Versioning

Major.Minor.Fix (or how npm expresses it Major.Minor.Patch)
(This Project should comply with Semver)

API Documentation

Please use the new guides and the new docs this here is just for “legacy reasons” and might be deleted later

Typegoose class

Since 6.0.0 deprecated, please try to remove it

###Methods

getModelForClass<T>(cl: T)

This method returns the corresponding Mongoose Model for the class (T). If no Mongoose model exists for this class yet, one will be created automatically (by calling the method setModelForClass).

setModelForClass<T>(cl: T)

This Method is Deprecated see Migrate to 6.0.0

Property decorators

Typegoose comes with TypeScript decorators, which responsibility is to connect the Mongoose schema behind the TypeScript class.

prop(options)

The prop decorator adds the target class property to the Mongoose schema as a property. Typegoose checks the decorated property’s type and sets the schema property accordingly. If another Typegoose extending class is given as the type, Typegoose will recognize this property as a sub document.

The options object accepts multiple config properties:

  • required: Just like the Mongoose required it accepts a handful of parameters. Please note that it’s the developer’s responsibility to make sure that if required is set to false then the class property should be optional.

    Note: for coding style (and type completion) you should use ! when it is marked as required

    // this is now required in the schema
    @prop({ required: true })
    firstName!: string;
    
    // by default, a property is not required
    @prop()
    lastName?: string; // using the ? optional property
    
  • index: Tells Mongoose whether to define an index for the property.

    @prop({ index: true })
    indexedField?: string;
    
  • unique: Just like the Mongoose unique, tells Mongoose to ensure a unique index is created for this path.

    // this field is now unique across the collection
    @prop({ unique: true })
    uniqueId?: string;
    
  • enum: The enum option accepts a string array. The class property which gets this decorator should have an enum-like type which values are from the provided string array. The way how the enum is created is delegated to the developer, Typegoose needs a string array which hold the enum values, and a TypeScript type which tells the possible values of the enum. However, if you use TS 2.4+, you can use string enum as well.

    enum Gender {
      MALE = 'male',
      FEMALE = 'female',
    }
    
    @prop({ enum: Gender })
    gender?: Gender;
    
  • lowercase: for strings only; whether to always call .toLowerCase() on the value.

    @prop({ lowercase: true })
    nickName?: string;
    
  • uppercase: for strings only; whether to always call .toUpperCase() on the value.

    @prop({ uppercase: true })
    nickName?: string;
    
  • trim: for strings only; whether to always call .trim() on the value.

    @prop({ trim: true })
    nickName?: string;
    
  • default: The provided value will be the default for that Mongoose property.

    @prop({ default: 'Nick' })
    nickName?: string;
    
  • _id: When false, no _id is added to the subdocument

    class Car {}
    
    @prop({ _id: false })
    car?: Car;
    
  • ref: By adding the ref option with another Typegoose class as value, a Mongoose reference property will be created. The type of the property on the Typegoose extending class should be Ref<T> (see Types section).

    class Car {}
    
    @prop({ ref: Car })
    car?: Ref<Car>;
    
  • refPath: Is the same as ref, only that it looks at the path specified, and this path decides which model to use

    class Car {}
    class Shop {}
    
    // in another class
    class Another {
      @prop({ required: true, enum: 'Car' | 'Shop' })
      which!: string;
    
      @prop({ refPath: 'which' })
      kind?: Ref<Car | Shop>;
    }
    
  • min / max (numeric validators): Same as Mongoose numberic validators.

    @prop({ min: 10, max: 21 })
    age?: number;
    
  • minlength / maxlength / match (string validators): Same as Mongoose string validators.

    @prop({ minlength: 5, maxlength: 10, match: /[0-9a-f]*/ })
    favouriteHexNumber?: string;
    
  • validate (custom validators): You can define your own validator function/regex using this. The function has to return a boolean or a Promise (async validation).

    // you have to get your own `isEmail` function, this is a placeholder
    
    @prop({ validate: (value) => isEmail(value)})
    email?: string;
    
    // or
    
    @prop({ validate: async (value) => { await isEmail(value) })
    email?: string;
    
    // or
    
    @prop({ validate: {
        validator: val => isEmail(val),
        message: `{VALUE} is not a valid email`
    }})
    email?: string;
    
    // or
    
    @prop({ validate: /\S+@\S+\.\S+/ })
    email?: string;
    
    // you can also use multiple validators in an array.
    
    @prop({ validate:
      [
        {
            validator: val => isEmail(val),
            message: `{VALUE} is not a valid email`
        },
        {
            validator: val => isBlacklisted(val),
            message: `{VALUE} is blacklisted`
        }
      ]
    })
    email?: string;
    
  • alias (alias): Same as Mongoose Alias, only difference is the extra property for type completion

    class Dummy {
      @prop({ alias: "helloWorld" })
      public hello: string; // will be included in the DB
      public helloWorld: string; // will NOT be included in the DB, just for type completion (gets passed as hello in the DB)
    }
    

Virtuals

  • Mongoose gives developers the option to create virtual properties. This means that actual database read/write will not occur these are just ‘calculated properties’. A virtual property can have a setter and a getter. TypeScript also has a similar feature which Typegoose uses for virtual property definitions (using the prop decorator).

    example:

    class Name {
      @prop()
      firstName?: string;
    
      @prop()
      lastName?: string;
    
      // this will create a virtual property called 'fullName'
      get fullName() {
        return `${this.firstName} ${this.lastName}`;
      }
      set fullName(full) {
        const [firstName, lastName] = full.split(' ');
        this.firstName = firstName;
        this.lastName = lastName;
      }
    }
    

    DB Document:

    {
      _id: ObjectId("<some long id>"),
      firstName: "Will",
      lastName: "Smith"
    }
    
  • Non-Virtuals are supported too:
    example:

    function setFullName(val: string[]): string {
      return val.join(' ');
    }
    
    function getFullname(val: string): string[] {
      return val.split(' ');
    }
    
    class Name {
      @prop({ set: setFullName, get: getFullname })
      fullname: string[]; // this is just for type completion & getting the type for the schema
    }
    
    ...
    await NameModel.create({ fullname: ['Will', 'Smith'] });
    const [first, last]: string[] = (await NameModel.findOne({}).exec()).fullname;
    

    DB Document:

    {
      _id: ObjectId("<some long id>"),
      fullname: "Will Smith"
    }
    
  • Virtual-Populate is also supported by doing

    class RefClass {
      @prop({ required: true, ref: Virtual })
      public refToName: Ref<Virtual>;
    }
    class Name {
      @prop({ ref: RefClass, foreignField: 'refToName', localField: '_id', justOne: false })
      public somevalue: Ref<RefClass>;
    }
    

Options (look here for more details):

  • ref: This is like a normal ref [Required]
  • foreignField: Which property(on the ref-Class) to match localField against [Required]
  • localField: Which property(on the current-Class) to match foreignField against [Required]
  • justOne: Return as One Document(true) or as Array(false) [Optional]
  • count: Return the number of Documents found instead of the actual Documents [Optional]

arrayProp(options)

The arrayProp is a prop decorator which makes it possible to create array schema properties.

The options object accepts required, enum and default, just like the prop decorator. In addition to these the following properties exactly one should be given:

  • items: This will tell Typegoose that this is an array which consists of primitives (if String, Number, or other primitive type is given) or this is an array which consists of subdocuments (if it’s extending the Typegoose class).

    @arrayProp({ items: String })
    languages?: string[];
    

Note that unfortunately the reflect-metadata API does not let us determine the type of the array, it only returns Array when the type of the property is queried. This is why redundancy is required here.

  • itemsRef: In mutual exclusion with items, this tells Typegoose that instead of a subdocument array, this is an array with references in it. On the Mongoose side this means that an array of Object IDs will be stored under this property. Just like with ref in the prop decorator, the type of this property should be Ref<T>[].

    class Car {}
    
    // in another class
    @arrayProp({ itemsRef: Car })
    previousCars?: Ref<Car>[];
    
  • itemsRefPath(IRP): Is the same as itemsRef only that it looks at the specified path of the class which specifies which model to use

    class Car {}
    class Shop {}
    
    // in another class
    class Another {
      @prop({ required: true, enum: ['Car', 'Shop'] })
      which!: string;
    
      @arrayProp({ itemsRefPath: 'which' })
      items?: Ref<Car | Shop>[];
    }
    

mapProp(options)

The mapProp is a prop decorator which makes it possible to create map schema properties.

The options object accepts enum and default, just like prop decorator. In addition to these the following properties are accepted:

  • of : This will tell Typegoose that the Map value consists of primitives (if String, Number, or other primitive type is given) or this is an array which consists of subdocuments (if it’s extending the Typegoose class).

    class Car {
      @mapProp({ of: Car })
      public keys?: Map<string, Car>;
    }
    

Method decorators

Method Decorators are deprecated see Migrate to 6.0.0

Class decorators

Mongoose allows the developer to add pre and post hooks / middlewares to the schema. With this it is possible to add document transformations and observations before or after validation, save and more.

Typegoose provides this functionality through TypeScript’s class decorators.

modelOptions

The Model Options can be used like below

@modelOptions({ existingMongoose, schemaOptions, existingConnection })
class Name {}

The Options for @modelOptions:

  • existingMongoose: mongoose: An existing Mongoose instance can also be passed down. If given, Typegoose uses this Mongoose instance’s model methods.
  • schemaOptions: mongoose.SchemaOptions: Additional schema options can be passed down to the schema-to-be-created.
  • existingConnection: mongoose.Connection: An existing Mongoose connection can also be passed down. If given, Typegoose uses this Mongoose instance’s model methods.

pre

We can simply attach a @pre decorator to the Typegoose class and define the hook function like you normally would in Mongoose. (Method supports REGEXP)

@pre<Car>('save', function(next) {
  if (this.model === 'Tesla') {
    this.isFast = true;
  }
  next();
})
class Car {
  @prop({ required: true })
  model!: string;

  @prop()
  isFast?: boolean;
}

This will execute the pre-save hook each time a Car document is saved. Inside the pre-hook Mongoose binds the actual document to this.

Note that additional typing information is required either by passing the class itself as a type parameter <Car> or explicity telling TypeScript that this is a Car (this: Car). This will grant typing informations inside the hook function.

post

Same as pre, the post hook is also implemented as a class decorator. Usage is equivalent with the one Mongoose provides. (Method supports REGEXP)

@post<Car>('save', (car) => {
  if (car.topSpeedInKmH > 300) {
    console.log(car.model, 'is fast!');
  }
})
class Car {
  @prop({ required: true })
  model!: string;

  @prop({ required: true })
  topSpeedInKmH!: number;
}

Of course this is not the document in a post hook (see Mongoose docs). Again typing information is required either by explicit parameter typing or by providing a template type.

plugin

Using the plugin decorator enables the developer to attach various Mongoose plugins to the schema. Just like the regular schema.plugin() call, the decorator accepts 1 or 2 parameters: the plugin itself, and an optional configuration object. Multiple plugin decorator can be used for a single Typegoose class.

If the plugin enhances the schema with additional properties or instance / static methods this typing information should be added manually to the Typegoose class as well.

import * as findOrCreate from 'mongoose-findorcreate';

@plugin(findOrCreate)
class User {
  // this isn't the complete method signature, just an example
  static findOrCreate(condition: DocumentType<User>):
    Promise<{ doc: DocumentType<User>, created: boolean }>;
}

const UserModel = getModelForClass(User);
const result = await UserModel.findOrCreate({ ... });

index

The @index decorator can be used to define advanced index types and index options not available via the index option of the @prop property decorator, such as compound indices, GeoJSON index types, partial indices, expiring documents, etc. Any values supported by MongoDB’s createIndex() are also valid for @index. For more info refer to interface IndexOptions

@index({ article: 1, user: 1 }, { unique: true })
@index({ location: '2dsphere' })
@index({ article: 1 }, { partialFilterExpression: { stars: { $gte: 4.5 } } })
class Location {
 @prop()
 article?: number;

 @prop()
 user?: number;

 @prop()
 stars?: number;

 @arrayProp({ items: Array })
 location?: [[Number]]
}

Types

Some additional types were added to make Typegoose more user friendly.
(for some additional types, that are not exported by default can be accessed via import * as types from 'typegoose/types')

DocumentType

This is basically the logical ‘and’ of the T and the mongoose.Document, so that both the Mongoose instance properties/functions and the user defined properties/instance methods are available on the instance.

ReturnModelType

This is the logical ‘and’ of mongoose.Model<DocumentType<T>> and T, so that the Mongoose model creates DocumentType<T> typed instances and all user defined static methods are available on the model.

Ref

For reference properties: Ref<T> - T if populated and ObjectID if unpopulated. -> there are TypeGuards for this to check named:

  • isDocument(T): returns true if T is populated, false otherwise
  • isDocumentArray(T): returns true if T is an Array AND is fully populated

Improvements

  • Add Tests for:
    • Hooks: add hook test for pre & post with error

Notes

  • mongoose is a peer-dependency, and a dev dependency to install it for dev purposes
  • Please dont add comments with +1 or something like that, use the Reactions
  • Typegoose cannot be used with classes of the same name, it will always return the first build class with that name
  • All Models in Typegoose are set to strict by default, and cant be changed!
  • npm run doc generates all documentation for all files that can be used as modules (is used for github-pages)
  • npm run doc:all generates documentation even for internal modules
  • This module should not be used with ts-node --transpile-only, --type-check must be added at least, szokodiakos#196

#typescript #mongodb

Yashi Tyagi

1617449307

CA Classes - Best CA Classes Online

Chartered Accountancy course requires mental focus & discipline, coaching for CA Foundation, CA Inter and CA Finals are omnipresent, and some of the best faculty’s classes have moved online, in this blog, we are going to give the best way to find online videos lectures, various online websites provide the CA lectures, Smartnstudy one of the best site to CA preparation, here all faculty’s video lecture available.

check here : ca classes

#ca classes online #ca classes in delhi #ca classes app #ca pendrive classes #ca google drive classes #best ca classes online

Why Use WordPress? What Can You Do With WordPress?

Can you use WordPress for anything other than blogging? To your surprise, yes. WordPress is more than just a blogging tool, and it has helped thousands of websites and web applications to thrive. The use of WordPress powers around 40% of online projects, and today in our blog, we would visit some amazing uses of WordPress other than blogging.
What Is The Use Of WordPress?

WordPress is the most popular website platform in the world. It is the first choice of businesses that want to set a feature-rich and dynamic Content Management System. So, if you ask what WordPress is used for, the answer is – everything. It is a super-flexible, feature-rich and secure platform that offers everything to build unique websites and applications. Let’s start knowing them:

1. Multiple Websites Under A Single Installation
WordPress Multisite allows you to develop multiple sites from a single WordPress installation. You can download WordPress and start building websites you want to launch under a single server. Literally speaking, you can handle hundreds of sites from one single dashboard, which now needs applause.
It is a highly efficient platform that allows you to easily run several websites under the same login credentials. One of the best things about WordPress is the themes it has to offer. You can simply download them and plugin for various sites and save space on sites without losing their speed.

2. WordPress Social Network
WordPress can be used for high-end projects such as Social Media Network. If you don’t have the money and patience to hire a coder and invest months in building a feature-rich social media site, go for WordPress. It is one of the most amazing uses of WordPress. Its stunning CMS is unbeatable. And you can build sites as good as Facebook or Reddit etc. It can just make the process a lot easier.
To set up a social media network, you would have to download a WordPress Plugin called BuddyPress. It would allow you to connect a community page with ease and would provide all the necessary features of a community or social media. It has direct messaging, activity stream, user groups, extended profiles, and so much more. You just have to download and configure it.
If BuddyPress doesn’t meet all your needs, don’t give up on your dreams. You can try out WP Symposium or PeepSo. There are also several themes you can use to build a social network.

3. Create A Forum For Your Brand’s Community
Communities are very important for your business. They help you stay in constant connection with your users and consumers. And allow you to turn them into a loyal customer base. Meanwhile, there are many good technologies that can be used for building a community page – the good old WordPress is still the best.
It is the best community development technology. If you want to build your online community, you need to consider all the amazing features you get with WordPress. Plugins such as BB Press is an open-source, template-driven PHP/ MySQL forum software. It is very simple and doesn’t hamper the experience of the website.
Other tools such as wpFoRo and Asgaros Forum are equally good for creating a community blog. They are lightweight tools that are easy to manage and integrate with your WordPress site easily. However, there is only one tiny problem; you need to have some technical knowledge to build a WordPress Community blog page.

4. Shortcodes
Since we gave you a problem in the previous section, we would also give you a perfect solution for it. You might not know to code, but you have shortcodes. Shortcodes help you execute functions without having to code. It is an easy way to build an amazing website, add new features, customize plugins easily. They are short lines of code, and rather than memorizing multiple lines; you can have zero technical knowledge and start building a feature-rich website or application.
There are also plugins like Shortcoder, Shortcodes Ultimate, and the Basics available on WordPress that can be used, and you would not even have to remember the shortcodes.

5. Build Online Stores
If you still think about why to use WordPress, use it to build an online store. You can start selling your goods online and start selling. It is an affordable technology that helps you build a feature-rich eCommerce store with WordPress.
WooCommerce is an extension of WordPress and is one of the most used eCommerce solutions. WooCommerce holds a 28% share of the global market and is one of the best ways to set up an online store. It allows you to build user-friendly and professional online stores and has thousands of free and paid extensions. Moreover as an open-source platform, and you don’t have to pay for the license.
Apart from WooCommerce, there are Easy Digital Downloads, iThemes Exchange, Shopify eCommerce plugin, and so much more available.

6. Security Features
WordPress takes security very seriously. It offers tons of external solutions that help you in safeguarding your WordPress site. While there is no way to ensure 100% security, it provides regular updates with security patches and provides several plugins to help with backups, two-factor authorization, and more.
By choosing hosting providers like WP Engine, you can improve the security of the website. It helps in threat detection, manage patching and updates, and internal security audits for the customers, and so much more.

Read More

#use of wordpress #use wordpress for business website #use wordpress for website #what is use of wordpress #why use wordpress #why use wordpress to build a website

Creola  Wehner

Creola Wehner

1598097120

How To Link Mongoose And Typescript for a Single Source of Truth | Hacker Noon

Typescript is awesome! Mongoose is popular.

Thanks to , we now have typescript definitions for mongoose, however getting both to talk can still be tricky.

There are a few tools out there to help us merge mongoose with typescript such as Typegoose and ts-mongoose, but, for one reason or another you may not want to use these tools and roll out without extra dependencies.

This post is a knowledge summary for what I believe to be a good way to build APIs with mongoose and typescript. I assume that the reader is already familiar with both individually.

Table of contents
Keeping a typescript interface and a mongoose schema in sync 🔗
Informing mongoose about our user type
Adding typesafety for Populate and Select
Including statics and instance methods
Splitting the user model into backend and frontend representations
Conclusion

#mongoose #mongodb #typescript #nodejs #model #schema

Carmen  Grimes

Carmen Grimes

1599326160

Reflecting business logic rules into the domain models using typescript — Part 3

In the previous article, we declare types and create a function to make and validate the type.

But it will take too much time if we want to create these functions and validators for each model we want to use.

In this article, we are going to use io-ts library to automate these boring tasks.

The final project is available here:

mohsensaremi/reflecting-business-logic-rules-into-the-domain-models

Contribute to mohsensaremi/reflecting-business-logic-rules-into-the-domain-models development by creating an account on…

github.com

Installation

yarn add io-ts fp-ts

So what is fp-ts ?

fp-ts is a functional programming utility for typescript and is a peer dependency for io-ts .

We are not going to talk about functional programming in this article.

We only discuss a little about Either monad.

Codec

io-ts represent our type with something called Codec.

A codec has decode and encode functions.

We create and validate our input data with decode function and put back the data in normal shape using encode function.

#domain-modeling #typescript #business-logic #domain-driven-design #domain-model