So, you’re on the TypeScript bandwagon and you want to use Mongodb and mongoose.
Great! So, now you need to define your schema and model. So, what is a good TypeScript developer to do?
Well, first we turn to npm to install the dependencies and declaration files.
This took me awhile to get right, so I hope it saves you some time.
Getting Started
We’ll start by using npm to install mongoose and the TypeScript declaration files using the new @types definitions with TypeScript 2.
$ npm install mongoose --save
$ npm install @types/mongoose --save-dev
$ npm install @types/mongodb --save-dev
Interface
First, let’s create a public interface for our model.
In this example I am going to be using a: User.
I created a new file at src/interfaces/user.ts:
export interface IUser {
email?: string;
firstName?: string;
lastName?: string;
}
This simply represents a user, and will be helpful when populating our mongodb collections.
Schema + Model
For the schema and model, I put both of these into a new file at src/schemas/user.ts:
import { Document, Schema, Model, model} from "mongoose";
import { IUser } from "../interfaces/user";
export interface IUserModel extends IUser, Document {
fullName(): string;
}
export var UserSchema: Schema = new Schema({
createdAt: Date,
email: String,
firstName: String,
lastName: String
});
UserSchema.pre("save", function(next) {
let now = new Date();
if (!this.createdAt) {
this.createdAt = now;
}
next();
});
UserSchema.methods.fullName = function(): string {
return (this.firstName.trim() + " " + this.lastName.trim());
};
export const User: Model<IUserModel> = model<IUserModel>("User", UserSchema);
Let’s dig into what is going on here:
- First, I am importing the
Document,SchemaandModelclasses, as well as the model() function. - I am also importing our
IUserinterface that we created previously. - Next, I create the
IUserModel. Note that it extends both theIUserinterface as well as theDocumentclass. - Next, I create the
Schema. Unfortunately, you have to list all of the schema properties again. This means that whenever you modify the schema, you will need to modify the interface as well, and vice-versa. - Next, I define a pre-save hook to populate the
created_atdate. - Next, I am defining a instance method. In this case I have a simple
fullname()method that returns the first name concatenated with the user’s last name. - Lastly, using the
UserSchemawe create a new model using mongoose’smodel()function.
In Action
Ok, now let’s see this in action. I have a simple REST API for the user defined in an expressjs app.
Here is the get() method that is routed to the /users/:id GET request.
/**
* Get a user
*
* @class UsersApi
* @method get
* @param req {Request} The express request object.
* @param res {Response} The express response object.
* @param next {NextFunction} The next function to continue.
*/
public get(req: Request, res: Response, next: NextFunction) {
//verify the id parameter exists
const PARAM_ID: string = "id";
if (typeof req.params[PARAM_ID] === "undefined" || req.params[PARAM_ID] === null) {
res.sendStatus(404);
next();
return;
}
//get the id
var id = req.params[PARAM_ID];
//get authorized user
this.authorize(req, res, next).then((user: IUserModel) => {
//make sure the user being deleted is the authorized user
if (user._id !== id) {
res.sendStatus(401);
next();
return;
}
//log
console.log(`[UsersApi.get] Retrieving user: {id: ${req.params.id}}.`);
//find user
User.findById(id).then((user: IUserModel) => {
//verify user was found
if (user === null) {
res.sendStatus(404);
next();
return;
}
//send json response
res.json(user);
next();
}).catch(next);
}).catch(next);
}
Here is what I am doing in the get() method:
- First, I verify that the id request parameter was provided. If not, the user get’s a 404 Not Found.
- Next, I have a method named “authorize” in a base class with the following signature:
authorize(req: Request, res: Response, next: NextFunction): Promise<IUserModel>. Note that it returns a Promise of our user model,IUserModel. - Assuming the user is authorized via a token sent in the headers with every request to the API we have a
IUserModelobject (similar to what we created above). - I think make sure that the authorized user is the one requesting to GET the user data. If not, sorry buddy, but you just get a 401 Unauthorized response.
- Ok, now that we made sure everything is in order, let’s do a bit of logging and then get the user from mongodb. Here I use the
Usermodel to find the document based on theidparameter. - Next, we verify that the user document returned is not null. If it is, then we send back a 404 Not Found response.
- Last, we return the json of the user document and call the
next()method.