Using TypeScript with MongoDB

Combine the power of TypeScript and MongoDB to create a scalable, production-grade database

Bret Cameron
Apr 14 · 9 min read

MongoDB is a versatile NoSQL database, commonly paired with Node.js. Instead of tables and rows, it uses collections and documents to store data. But what makes it so versatile — the fact that no schema is required — can also make it more difficult to scale and maintain, particularly in a large, production-grade application.

This is where tools like Mongoose and TypeScript come in handy. While many MongoDB tutorials cover Mongoose, far fewer discuss how to introduce TypeScript into your MongoDB projects. That’s why I’m writing this article. In it, we’ll discuss ways to combine MongoDB with TypeScript, bringing the advantages of type-checking to our database.

The tutorial is not an introduction to either MongoDB or TypeScript, but rather provides a strategy for using them together. For those who want to skip straight to the finished code, there’s a repo link at the bottom!

1. Setting Up MongoDB

This article assumes you know how to install MongoDB, create a database and connect to it. If you’re unsure about that or need a quick refresher, check out my article MongoDB: A Beginner’s Guide, which covers setting up a database in the cloud using MongoDB Atlas.

You’ll need to grab your database’s connection URI to continue with this article. This can be copied from the MongoDB Atlas, and it should look something like this:

Make sure to replace <username> and <password> with your own credentials.

For simplicity, I recommend using the cloud version of MongoDB. But if you’d prefer to run MongoDB locally, you’ll need to make sure you have MongoDB installed and (probably) MongoDB Compass — a GUI for the database. Before proceeding with the tutorial, run mongod in the terminal. By default, your connection URI will be mongodb://localhost:27017/test . If you need something other than the default, check out the official reference.

Note: If you’re on macOS Catalina, you may have to go through some additional steps to get up-and-running locally for the first time. I wrote an article about this here.

2. Setting Up Your Project

We’ll begin by initialising the project, by typing yarn init (or npm init ) into the terminal. When prompted, set the entry point to src/server.ts .

Next, we’ll install our dependencies, using yarn add (or npm install ):

We’ll be using Express to make our server and Mongoose to create our MongoDB schema and connect to MongoDB. Nodemon speeds up development, by automatically restarting the node application when a file changes. TypeScript allows us to create TypeScript files, and ts-node allows us to execute those files in Node. Finally, we need to add in types for Express, Mongoose and Node.

3. Configurations

Before we can get coding, we need to create a development script in package.json . I suggest using:

Above, I updated "main" to src/server.ts .

I also added a script called "script" , which we’ll use to trigger dev scripts later in the tutorial.

We can configure the TypeScript compiler by creating a tsconfig.json file in the root directory. This will tell the compiler where to look for TypeScript files and it will allow us to use ES6 import / export syntax. Here’s a configuration to get us started:

4. Connecting to the Database

It’s time to start writing code!

We’ll start by connecting to our database. It’ll be useful, later on, to create a separate file to initialise our database, which we’ll put inside src/database :

In this file, we’ll import Mongoose and create a function to connect to (and another to disconnect from) our database. This is where we’ll use whatever URI we chose in section one.

Now, we can go back into the src folder and create the file that will start our server: server.ts .

With the database logic elsewhere, our server can be very simple:

5. Creating Models

This is where we can begin to benefit from TypeScript. There are lots of different possibilities when it comes to structuring your app. But, as there are usually three parts to a Mongoose model (schema, static methods and instance methods), I recommend splitting these into three separate files. We can add a fourth file to hold our TypeScript interfaces and a fifth to bring everything together.

So, let’s create a folder inside src called database/users and — in the users directory — create the five files:

  • users.schema.ts — where we define the Mongoose Schema , which determines the shape of our MongoDB documents
  • users.statics.ts — where we define the static methods of our model, functions which can be called on the model itself
  • users.methods.ts — where we define the instance methods of our model, functions which can be called on individual model instances
  • users.types.ts — where we store the types we’ll use in the other files
  • users.model.ts — where we’ll bring everything together.

Here’s a shell command to create those files:

We’ll start with the schema and give our user document five fields:

So far, this is all Mongoose. Now let’s create a TypeScript interface to represent our schema.

In this file, we’ll define an interface containing the same fields as the schema. We’ll also want to extend this as part of two more interfaces:

  • IUserDocument — which includes our fields, plus the other elements of a standard Mongoose Document
  • IUserModel — which represents a standard Mongoose Model, containing documents of our IUserDocument type.

Later, we can add methods to the IUserDocument and IUserModel interfaces.

Before adding any methods, let’s combine the schema and types we’ve created so far.

We now have a working, type-checked Mongoose model!

Now, what if we wanted to add methods to our model?

Static methods are executed on the model itself, rather than on particular instances. We can define static methods in their own file, using the model as the first argument, and later pass them into users.schema.ts .

Let’s create two static methods:

  • findOneOrCreate , which checks to see if an entry exists and — if it doesn’t — creates a new entry.
  • findByAge , which returns an array of users, based on a provided age range.

We’ll also need to import the IUserDocument and IUserModel interfaces we created earlier:

Instance methods refer to particular instances of the model. For example, if we had a particular user called Joe Bloggs, we could perform operations based on any of the fields related to his document in the database.

We’ll also create two instance methods:

  • setLastUpdated — which will update a particular entry’s lastUpdated field to the current time.
  • sameLastName — which will find all entries with the same last name as the document.

Now, let’s return to users.schema.ts to add in our new methods:

We also need to type check our new methods inside users.types.ts :

We now have a working Mongoose model — complete with static and instance methods — and everything is type-checked. This process may add a little time and complexity to the development of our model, but it will be much easier to maintain and debug as we grow our application!

6. Adding Users

That’s pretty much everything we’ll need to get working with our model. But so far, we’ve not yet worked with any data.

Let’s create a script to add some dummy data to our database. We’ll start by making a scripts folder and adding a function called createDummyData.ts .

Before you run this script, make sure you have ts-node and typescript installed globally:

Then, we can run yarn script createDummyData to populate our database with a few new users.

If you open up MongoDB Atlas (or MongoDB compass), you should now see a collection called users with 10 new documents!

MongoDB Atlas collection containing test data
MongoDB Atlas collection containing test data
If you’re using MongoDB Atlas, you should see something like this

7. Testing Our Methods

Finally, now that we have some users, we can see our methods in action. To do this, I’ll create another script in our scripts folder, simply called test.ts . Here’s some code which makes use of every static and instance method we created earlier:

Run yarn script test and, if everything’s set up correctly, you should see the expected results logged to the console!

I hope this has provided a useful example of how to get the most out of MongoDB and Typescript. To see a working example of the code we’ve created, check out this repo:

The Startup

Medium's largest active publication, followed by +730K people. Follow to join our community.

Bret Cameron

Written by

Writer and developer based in London. On Medium, I write about JavaScript and web development 💻

The Startup

Medium's largest active publication, followed by +730K people. Follow to join our community.

Bret Cameron

Written by

Writer and developer based in London. On Medium, I write about JavaScript and web development 💻

The Startup

Medium's largest active publication, followed by +730K people. Follow to join our community.

Medium is an open platform where 170 million readers come to find insightful and dynamic thinking. Here, expert and undiscovered voices alike dive into the heart of any topic and bring new ideas to the surface. Learn more

Follow the writers, publications, and topics that matter to you, and you’ll see them on your homepage and in your inbox. Explore

If you have a story to tell, knowledge to share, or a perspective to offer — welcome home. It’s easy and free to post your thinking on any topic. Write on Medium

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store