# Mongoose Contributors are welcome
This tutorial show yous how you can use mongoose package with Ts.ED.
# Feature
Currently, @tsed/mongoose
allows you to:
- Configure one or more MongoDB database connections via the
@ServerSettings
configuration. All databases will be initialized when the server starts during the server'sOnInit
phase. - Declare a Model from a class with annotation,
- Add a plugin, PreHook method and PostHook on your model
- Inject a Model to a Service, Controller, Middleware, etc.
- Create and manage multiple connections v5.35.0
Note
@tsed/mongoose
uses the JsonSchema and its decorators to generate the mongoose schema.
# Installation
Before using the @tsed/mongoose
package, we need to install the mongoose module.
npm install --save mongoose
npm install --save @tsed/mongoose
npm install --save-dev @types/mongoose
npm install --save-dev @tsed/testing-mongoose
2
3
4
Then import @tsed/mongoose
in your Configuration:
import {Configuration} from "@tsed/common";
import "@tsed/mongoose"; // import mongoose ts.ed module
import "@tsed/platform-express";
@Configuration({
mongoose: [
{
id: "default", // Recommended: define default connection. All models without dbName will be assigned to this connection
url: "mongodb://127.0.0.1:27017/default",
connectionOptions: {}
},
{
id: "db2",
url: "mongodb://127.0.0.1:27017/db2",
connectionOptions: {}
}
]
})
export class Server {
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# MongooseService
lets you to retrieve an instance of Mongoose.Connection.
import {Service} from "@tsed/common";
import {MongooseService} from "@tsed/mongoose";
@Service()
export class MyService {
constructor(mongooseService: MongooseService) {
const default = mongooseService.get(); // OR mongooseService.get("default");
// GET Other connection
const db2 = mongooseService.get('db2');
}
}
2
3
4
5
6
7
8
9
10
11
# API
Ts.ED gives some decorators and services to write your code:
- DynamicRef
- Indexed
- Model
- MongooseIndex
- MongoosePlugin
- ObjectID
- PostHook
- Ref
- Schema
- MongooseSchema
- SchemaIgnore
- Select
- Trim
- Unique
- VirtualRef
You can also use the common decorators to describe model (See models documentation):
- ObjectID
- Deprecated
- Security
- Summary
- Schema
- Example
- Name
- AdditionalProperties
-
AllowTypes - Any
- Const
- Default
- Description
- Enum
- ExclusiveMaximum
- Format
-
IgnoreProperty - Ignore
- Integer
- MaxItems
- MaxLength
- Maximum
- MinItems
- MinLength
- Minimum
- MultipleOf
- Pattern
-
PropertyDeserialize - OnDeserialize
- PropertyFn
-
PropertyName -
PropertySerialize - OnSerialize
-
PropertyType - CollectionOf
- Title
- UniqueItems
- Allow
- Required
# Declaring a Mongoose object (schema or model)
# Declaring a Model
@tsed/mongoose
works with models which must be explicitly declared.
import {Property} from "@tsed/common";
import {Model, ObjectID} from "@tsed/mongoose";
@Model()
export class MyModel {
@ObjectID("id")
_id: string;
@Property()
unique: string;
}
2
3
4
5
6
7
8
9
10
11
# Declaring a Model to a specific connection
import {Property} from "@tsed/common";
import {Model, ObjectID} from "@tsed/mongoose";
@Model({
connection: "db2"
})
export class MyModel {
@ObjectID("id")
_id: string;
@Property()
unique: string;
}
2
3
4
5
6
7
8
9
10
11
12
13
# Declaring a Schema
@tsed/mongoose
supports subdocuments which must be explicitly declared.
import {Property} from "@tsed/common";
import {Schema} from "@tsed/mongoose";
@Schema()
export class MyModel {
@Property()
unique: string;
}
2
3
4
5
6
7
8
TIP
Schema decorator accepts a second parameter to configure the Schema (See Mongoose Schema)
# Declaring Properties
By default, @tsed/mongoose
reuses the metadata stored by the decorators dedicated
to describe a JsonSchema. These decorators come from the @tsed/common
package.
import {Default, Enum, Format, Ignore, Maximum, MaxLength, Minimum, MinLength, Pattern, Required} from "@tsed/common";
import {Indexed, Model, ObjectID, Unique} from "@tsed/mongoose";
enum Categories {
CAT1 = "cat1",
CAT2 = "cat2"
}
@Model()
export class MyModel {
@Ignore() // exclude _id from mongoose in the generated schema
_id: string;
@ObjectID("id") // Or rename _id by id (for response sent to the client)
_id: string;
@Unique()
@Required()
unique: string;
@Indexed()
@MinLength(3)
@MaxLength(50)
indexed: string;
@Minimum(0)
@Maximum(100)
@Default(0)
rate: Number = 0;
@Enum(Categories)
// or @Enum("type1", "type2")
category: Categories;
@Pattern(/[a-z]/) // equivalent of match field in mongoose
pattern: String;
@Format("date-time")
@Default(Date.now)
dateCreation: Date = new Date();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
TIP
It isn't necessary to use decorator on property when you use one of these decorators:
These decorators call automatically the decorator.
# Collections
Mongoose and @tsed/mongoose
support both lists and maps.
import {CollectionOf} from "@tsed/common";
import {Model} from "@tsed/mongoose";
@Model()
export class MyModel {
@CollectionOf(String)
list: string[];
@CollectionOf(String)
map: Map<string, string>; // key must be a string.
}
2
3
4
5
6
7
8
9
10
11
# Subdocuments
@tsed/mongoose
supports mongoose
subdocuments as long as they are defined schemas. Therefore, subdocuments must be decorated by @Schema()
.
import {CollectionOf, Property} from "@tsed/common";
import {Model, ObjectID, Schema} from "@tsed/mongoose";
@Schema()
export class MySchema {
@ObjectID("id")
_id: string;
@Property()
name: string;
}
@Model()
export class MyModel {
@ObjectID("id")
_id: string;
@Property()
schema: MySchema;
@CollectionOf(MySchema)
schemes: MySchema[];
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# References
@tsed/mongoose
supports mongoose
references between defined models.
import {Model, ObjectID, Ref} from "@tsed/mongoose";
@Model()
export class MyRef {
@ObjectID("id")
_id: string;
}
@Model()
export class MyModel {
@Ref(MyRef)
ref: Ref<MyRef>;
@Ref(MyRef)
refs: Ref<MyRef>[];
@Ref(MyRef)
refs: Map<string, MyRef>;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Be aware of circular dependencies. Direct references must be declared after the referred class has been declared. This means the reference cannot know the referred class directly at runtime.
import {Required} from "@tsed/common";
import {Model, ObjectID, Ref} from "@tsed/mongoose";
@Model()
export class User {
@ObjectID("id")
_id: string;
@Required()
name: string;
@Ref("Workspace") // Give workspace as string.
workspace: Ref<Workspace>;
}
@Model()
export class Workspace {
@ObjectID("id")
_id: string;
@Required()
name: string;
@Ref(User)
createdBy: Ref<User>;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# Virtual References
@tsed/mongoose
supports mongoose
virtual references between defined models.
Be aware of circular dependencies. Direct references must be declared after the referred class has been declared. This means the virtual reference cannot know the referred class directly at runtime.
import {Property} from "@tsed/common";
import {Model, Ref, VirtualRef, VirtualRefs} from "@tsed/mongoose";
@Model()
class Person {
@Property()
name: string;
@Property()
band: string;
}
@Model()
class Band {
@VirtualRef({
ref: Person, // The model to use
localField: "name", // Find people where `localField`
foreignField: "band", // is equal to `foreignField`
// If `justOne` is true, 'members' will be a single doc as opposed to
// an array. `justOne` is false by default.
justOne: false,
options: {} // Query options, see http://bit.ly/mongoose-query-options
})
members: VirtualRefs<Person>;
}
@Model()
export class MyRef {
@VirtualRef("MyModel")
virtual: VirtualRef<MyModel>;
@VirtualRef("MyModel")
virtuals: VirtualRefs<MyModel>;
}
@Model()
export class MyModel {
@Ref(MyRef)
ref: Ref<MyRef>;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# Dynamic References
@tsed/mongoose
supports mongoose
dynamic references between defined models.
This works by having a field with the referenced object model's name and a field with the referenced field.
import {Model, Ref, DynamicRef} from "@tsed/mongoose";
import {Enum, Required} from "@tsed/common"
@Model()
export class DynamicRef {
@DynamicRef('type')
dynamicRef: Ref<ModelA | ModelB>
@Enum(['Mode lA', 'ModelB'])
type: string // This field has to match the referenced model's name
}
2
3
4
5
6
7
8
9
10
# Register hook
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.
Ts.ED provides class decorator to register middlewares on the pre and post hook.
# Pre hook
We can simply attach a decorator to the model class and define the hook function like we would normally do in Mongoose.
import {Required} from "@tsed/common";
import {Model, ObjectID, PreHook} from "@tsed/mongoose";
@Model()
@PreHook("save", (car: CarModel, next: any) => {
if (car.model === "Tesla") {
car.isFast = true;
}
next();
})
export class CarModel {
@ObjectID("id")
_id: string;
@Required()
model: string;
@Required()
isFast: boolean;
// or Prehook on static method
@PreHook("save")
static preSave(car: CarModel, next: any) {
if (car.model === "Tesla") {
car.isFast = true;
}
next();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
This will execute the pre-save hook each time a CarModel
document is saved.
# Post hook
We can simply attach a decorator to the model class and define the hook function like we would normally do in Mongoose.
import {Required} from "@tsed/common";
import {Model, ObjectID, PostHook} from "@tsed/mongoose";
@Model()
@PostHook("save", (car: CarModel) => {
if (car.topSpeedInKmH > 300) {
console.log(car.model, "is fast!");
}
})
export class CarModel {
@ObjectID("id")
_id: string;
@Required()
model: string;
@Required()
isFast: boolean;
// or Prehook on static method
@PostHook("save")
static postSave(car: CarModel) {
if (car.topSpeedInKmH > 300) {
console.log(car.model, "is fast!");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
This will execute the post-save hook each time a CarModel
document is saved.
# Plugin
Using the 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 model class.
import {Inject, Service} from "@tsed/common";
import {Model, MongooseModel, MongoosePlugin} from "@tsed/mongoose";
import * as findOrCreate from "mongoose-findorcreate";
import {User} from "./User";
@Model()
@MongoosePlugin(findOrCreate)
class UserModel {
// this isn't the complete method signature, just an example
static findOrCreate(condition: InstanceType<User>):
Promise<{doc: InstanceType<User>, created: boolean}>;
}
@Service()
class UserService {
constructor(@Inject(UserModel) userModel: MongooseModel<UserModel>) {
userModel
.findOrCreate({...})
.then(findOrCreateResult => {
// ...
});
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# Inject model
It's possible to inject a model into a Service (or Controller, Middleware, etc...):
import {Inject, Service} from "@tsed/common";
import {MongooseModel} from "@tsed/mongoose";
import {MyModel} from "./models/MyModel";
@Service()
export class MyService {
constructor(@Inject(MyModel) private model: MongooseModel<MyModel>) {
console.log(model); // Mongoose.model class
}
async save(obj: MyModel): Promise<MongooseModel<MyModel>> {
const doc = new this.model(obj);
await doc.save();
return doc;
}
async find(query: any) {
const list = await this.model.find(query).exec();
console.log(list);
return list;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
TIP
You can find a working example on Mongoose here.
# Testing beta v5.35.0
The package @tsed/testing-mongoose
allows you to test your server with a memory database.
TIP
This package uses the amazing mongodb-memory-server to mock the mongo database.
# Testing API
This example shows you how you can test your Rest API with superagent and a mocked Mongo database:
# Testing Model
This example shows you how can test the model:
- Session & cookies
- Passport.js
- TypeORM
- Mongoose
- GraphQL
- Socket.io
- Swagger
- AJV
- Multer
- Serve static files
- Templating
- Throw HTTP Exceptions
- Customize 404
- AWS
- Jest
- Seq
- Controllers
- Providers
- Model
- Converters
- Middlewares
- Pipes
- Interceptors
- Authentication
- Hooks
- Injection scopes
- Custom providers
- Custom endpoint decorator
- Testing