Skip to main content

Quick Start Guide

Quick Overview of Typegoose

note

This Guide is for Typegoose version ~9.0

Typegoose is a "wrapper" for easily writing Mongoose models with TypeScript.

Instead of writing this:

// This is a representation of how typegoose's compile output would look
interface Car {
model?: string;
}

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

interface User {
name?: string;
age!: number;
preferences?: string[];
mainJob?: Job;
jobs?: Job[];
mainCar?: Car | string;
cars?: (Car | string)[];
}

const JobSchema = new mongoose.Schema({
title: String;
position: String;
});

const CarModel = mongoose.model('Car', {
model: String,
});

const UserModel = mongoose.model('User', {
name: { type: String },
age: { type: Number, required: true },
preferences: [{ type: String }],
mainJob: { type: JobSchema },
jobs: [{ type: JobSchema }],
mainCar: { type: Schema.Types.ObjectId, ref: 'Car' },
cars: [{ type: Schema.Types.ObjectId, ref: 'Car' }],
});

You can just write this:

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

@prop()
public position?: string;
}

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

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

@prop({ required: true })
public age!: number; // This is a single Primitive

@prop({ type: () => [String] })
public preferences?: string[]; // This is a Primitive Array

@prop()
public mainJob?: Job; // This is a single SubDocument

@prop({ type: () => Job })
public jobs?: Job[]; // This is a SubDocument Array

@prop({ ref: () => Car })
public mainCar?: Ref<Car>; // This is a single Reference

@prop({ ref: () => Car })
public cars?: Ref<Car>[]; // This is a Reference Array
}
caution

type has to be defined when working with Arrays, because Reflection only returns basic information. Look here for why
Like public: string[] is in reflection only Array.

Look here for what ! means on an property
Look here for what ? means on an property

How to Start using typegoose

Requirements

  • TypeScript version ^4.4 (since 9.0) is recommended, though older ones may also work
  • NodeJS ^12.22.0
  • Mongoose ~6.0.11
  • A IDE that supports TypeScript linting is recommended to be used (VSCode is recommended)
  • This Guide expects you to know how Mongoose (or at least its models) works
  • experimentalDecorators and emitDecoratorMetadata must be enabled in tsconfig.json
  • tsconfig option target being es6 (or higher)
info

tsconfig option emitDecoratorMetadata is not strictly requierd, look here for more

Install

npm install --save @typegoose/typegoose # install typegoose itself

npm install --save mongoose # install peer-dependency mongoose

How to use Typegoose

Let's say you have a Mongoose model like this one:

const kittenSchema = new mongoose.Schema({
name: String
});

const KittenModel = mongoose.model('Kitten', kittenSchema);

let document = await KittenModel.create({ name: 'Kitty' });
// "document" has no types

With Typegoose, it can be converted to something like:

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

const KittenModel = getModelForClass(KittenClass);

let document = await KittenModel.create({ name: 'Kitty' });
// "document" has proper types of KittenClass
note

new KittenModel({}) has no types of KittenClass, because Typegoose doesn't modify functions of Mongoose, read more here

Do's and Don'ts of Typegoose

  • Typegoose is a wrapper for Mongoose's models
  • Typegoose does not modify any functions of Mongoose
  • Typegoose aims to get Mongoose's models to be stable through type-information from classes (without defining extra interfaces)
  • Typegoose aims to make Mongoose more usable by making the models more type-rich with TypeScript
  • Decorated schema configuration classes (like KittenClass above) must use explicit type declarations

Extra Examples

Static Methods

Sometimes extra functions for model creation or pre-written querys are needed, they can be done as follows:

class KittenClass {
@prop()
public name?: string;

@prop()
public species?: string;

// the "this" definition is required to have the correct types
public static async findBySpecies(this: ReturnModelType<typeof KittenClass>, species: string) {
return this.find({ species }).exec();
}
}
const KittenModel = getModelForClass(KittenClass);

const docs = await KittenModel.findBySpecies('SomeSpecies');
note

pre-6.0 static functions needed @staticMethod, but this is not needed anymore.

Instance Methods

Sometimes extra functions for manipulating data on an instance is needed, they can be done as follows:

class KittenClass {
@prop()
public name?: string;

@prop()
public species?: string;

// the "this" definition is required to have the correct types
public async setSpeciesAndSave(this: DocumentType<KittenClass>, species: string) {
this.species = species;
await this.save();
}
}
const KittenModel = getModelForClass(KittenClass);

const doc = new KittenModel({ name: 'SomeCat', species: 'SomeSpecies' });
await doc.setSpeciesAndSave('SomeOtherSpecies');
note

Pre-6.0 static functions needed @instanceMethod, but this is not needed anymore.

Hooks

Typegoose also supports hooks. They can be used like this:

@pre<KittenClass>('save', function() {
this.isKitten = this.age < 1
})
@post<KittenClass>('save', function(kitten) {
console.log(kitten.isKitten ? 'We have a kitten here.' : 'We have a big kitty here.')
})
class KittenClass {
@prop()
public name?: string;

@prop()
public species?: string;

@prop()
public age?: number

@prop({ default: false })
public isKitten?: boolean
}

const KittenModel = getModelForClass(KittenClass);

const doc = new KittenModel({ name: 'SomeCat', species: 'SomeSpecies', age: 0 });
await doc.save(); // this should output "We have a kitten here."
const doc = new KittenModel({ name: 'SomeCat', species: 'SomeSpecies', age: 2 });
await doc.save(); // this should output "We have a big kitty here."

For detailed explanation of Hooks, please see Hooks.

Note:

  • Do not use Arrow Functions, because it will break the binding of this
  • For ESLint users: Make sure that rule eslint-no-use-before-defining is disabled, otherwise you might get ESLint errors / warnings inside the hooks