mongoose typescript models - Appsilon/styleguide GitHub Wiki

Creating Mongoose models with TypeScript

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.

Designing a POJO interface

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.

Adding Mongoose functionality

Declaring POJO and Mongoose mixin

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<IUserModel>("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.

⚠️ **GitHub.com Fallback** ⚠️