mongoose typescript models - Appsilon/styleguide GitHub Wiki
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;
We will 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…
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
});
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<IUserModel>("User", userSchema);
And this is what we're going to export.
export = User;
Let's save this file as User.ts
.
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!
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.