-
Notifications
You must be signed in to change notification settings - Fork 9
mongoose typescript models
If you're developing a project in TypeScript, it seems natural to expect that all models you use are strongly-typed. On the other side, you may often need to have them loosely coupled to the backend - for example to share them between client and server or multiple applications. There is a way to design your models so that they are interchangeable when needed, yet useful when you need full-fledged Mongoose ORM functionality.
For this guide, we'll want to design an User
model. Let's start with creating the POJO interface for all User
objects:
interface IUser {
email: string;
password: string;
displayName: string;
};
export = IUser;
I'll refer to this file further as IUser.ts
.
In a separate file, we'll want to enable Mongoose ORM management for all User
objects. To do so, we'll need to declare a mixin of our IUser
and mongoose.Document
- the latter interface contains ORM-based functions such as save()
.
import mongoose = require("mongoose");
import IUser = require("../../shared/Users/IUser");
interface IUserModel extends IUser, mongoose.Document { }
```
This `IUserModel` now can be passed as a type argument for Mongoose's model function. But first…
### Declaring Mongoose schema for `User` ###
This is needed, even though it seems like it's redundant - TypeScript does not preserve runtime type information, so it's lost in the transpiling phase. On the other hand, the object model in a database does not need to match an in-memory representation exactly - we may want to add some other, "technical" properties.
So now let's declare our schema:
var userSchema = new mongoose.Schema({ email: String, password: String, displayName: String });
### Wrapping it up together with `Model` ###
Now that we've got all parts together, we'll want to have an actual implementation of our interface. Fortunately, all we have to do is call the `mongoose.model<T>` method, passing the schema. Please note that the `T` type argument must derive from `mongoose.Document` - that's why we'll pass our mixin:
var User = mongoose.model("User", userSchema);
And this is what we're going to export.
export = User;
Let's save this file as `User.ts`.
## Using the model in server code ##
The model that we have just designed can be used just like any other Mongoose model. All we have to do is load our `User.ts` file:
import User = require("./User");
var user = new User({email: "[email protected]"}); user.save();
This `user` variable implements the `IUser` interface and can be used in all functions, that previously referred to our POJO objects.
function doSthWithUser(user: IUser) { }
doSthWithUser(user); // works!
## Using model in client-side TypeScript ##
We can pass our object to client (e.g. serialized using JSON) using model's `toJSON()` method. The receiver callback on client-side should then validate the object (or assume it's compliant with the schema), and then cast it to desired POJO type (`IUser`). Note that casting itself does not imply any type-checking.