NestJS Mongoose 101: Building Scalable Applications Simplified

By: Published: February 18, 2022

NestJS Mongoose - Featured Image

MongoDB has become a popular choice for growing businesses globally to efficiently handle their rapidly growing data needs. It is a scalable, flexible, and reliable Open-source NoSQL database management solution. Today, NestJS has become a widely used framework for building scalable applications. Powered by Typescript, NestJS offers complete support for databases like MySQL, PostgreSQL, and MongoDB. Using the NestJS Mongoose package, you can easily connect to the MongoDB database and build scalable applications. Mongoose is one of the popular MongoDB object modeling tools.

In this article, you will learn how to effectively build a scalable application using NestJS Mongoose in 5 simple steps. 

Table of Contents

What is MongoDB?

NestJS Mongoose - MongoDB Logo
Image Source

MongoDB is a popular free and open-source cross-platform document-oriented database built for efficiently storing and processing massive volumes of data. Unlike traditional relational databases, MongoDB is classified as a NoSQL Database Management System that uses Collections and JSON-like Documents instead of tables consisting of rows and columns. Each collection consists of multiple documents that contain the basic units of data in terms of key and value pairs. 

Officially introduced as an open-source development model in 2009, the MongoDB database is designed, maintained, and managed by MongoDB.Inc under a combination of the Server Side Public License and the Apache License. MongoDB is widely used by organizations such as MetLife, Barclays, Viacom,  New York Times, Facebook, Nokia, eBay, Adobe, Google, etc to efficiently meet their exponentially growing data processing and storage requirements. MongoDB is highly flexible as it supports several programming languages such as C, C++, C#, Go, Java, Node.js, Perl, PHP, Python, Motor, Ruby, Scala, Swift, and Mongoid.

Key Features of MongoDB

NestJS Mongoose - MongoDB Features
Image Source

With constant efforts from the online community, MongoDB has evolved over the years. Some of its eye-catching features are:

  • High Data Availability & Stability: MongoDB’s Replication feature provides multiple servers for disaster recovery and backup. Since several servers store the same data or shards of data, MongoDB provides greater data availability & stability. This ensures all-time data access and security in case of server crashes, service interruptions, or even good old hardware failure. 
  • Accelerated Analytics: You may need to consider thousands to millions of variables while running Ad-hoc queries. MongoDB indexes BSON documents and utilizes the MongoDB Query Language (MQL) that allows you to update Ad-hoc queries in real-time. MongoDB provides complete support for field queries, range queries, and regular expression searches along with user-defined functions.
  • Indexing: With a wide range of indices and features with language-specific sort orders that support complex access patterns to datasets, MongoDB provides optimal performance for every query. For the real-time ever-evolving query patterns and application requirements, MongoDB also provisions On-demand Indices Creation.
  • Horizontal Scalability: With the help of Sharding, MongoDB provides horizontal scalability by distributing data on multiple servers using the Shard Key. Each shard in every MongoDB Cluster stores parts of the data, thereby acting as a separate database. This collection of comprehensive databases allows efficient handling of growing volumes of data with zero downtime. The complete Sharding Ecosystem is maintained and managed by Mongos that directs queries to the correct shard based on the Shard Key.
  • Load Balancing: Real-time Replication and Sharding contribute towards large-scale Load Balancing. Ensuring top-notch Concurrency Controls and Locking Protocols, MongoDB can effectively handle multiple concurrent read and write requests for the same data.  
  • Aggregation: Similar to the SQL Group By clause, MongoDB can easily batch process data and present a single result even after executing several other operations on the group data. MongoDB’s Aggregation framework consists of 3 types of aggregations i.e. Aggregation Pipeline, Map-Reduce Function, and Single-Purpose Aggregation methods.

What is NestJS?

NestJS Mongoose - Nest Logo
Image Source

NestJS is an open-source, extensible, versatile, & progressive Node.js framework. Built on top of Express.js & Typescript, NestJS allows you to write scalable, testable, and loosely coupled applications. This simple yet powerful tool is currently the fastest-growing Node.Js framework in TypeScript used for creating compelling and demanding backend systems.

It offers complete support for databases such as PostgreSQL, MongoDB & MySQL. For instance, you can use the NestJS Mongoose package that easily connects to MongoDB and serves as a MongoDB object modeling tool. NestJS also combines the features of Object-Oriented Programming (OOP), Functional Programming (FP) as well as Functional Reactive Programming (FRP).

Key Features of NestJS

Since its inception in 2017, NestJS has become one of the popular frameworks for building scalable applications. This is due to its following eye-catching features:

  • Dependency Injection: NestJS offers a built-in dependency injection container with support for factory injection also.
  • Easy to Use: With a robust CLI for productivity-boosting, NestJS is easy to use, learn & master. Since it is also similar to Spring & .NET, developers familiar with these technologies can apply hard-learned patterns and best practices here. NestJS’s structural development approach assists large teams in creating complex backend systems more consistently. 
  • Integration Support: Using specific modules, NestJS allows seamless integrations with common technologies and concepts such as GraphQL, TypeORM, Mongoose, Logging, Caching, Validation, Web Sockets, etc.
  • Freely Available: It is an MIT-licensed open-source project. Over the years, NestJS has developed a large online community and support system. For any assistance, you can also check out its detailed and well-maintained documentation.
  • Angular Influenced: Based on Angular, NestJS provides a simple structure that allows more attention towards the design of endpoints and their consumers. NestJS lays down a specific architecture by introducing Angular-like modules, services, and controllers, ensuring the application is scalable, highly testable, and loosely coupled. This is good when compared to other Node.js frameworks like Express or Koa where a mistake early on in the project regarding the architecture may require a lot of time for refactoring the codebase later.
Simplify MongoDB ETL with Hevo’s No-code Data Pipeline

Hevo Data, a No-code Data Pipeline helps to load data from any data source such as Databases, SaaS applications, Cloud Storage, SDK,s, and Streaming Services and simplifies the ETL process. It supports MongoDB & MongoDB Atlas, along with 100+ data sources (Including 40+ Free Data Sources), and is a 3-step process by just selecting the data source, providing valid credentials, and choosing the destination. Hevo not only loads the data onto the desired Data Warehouse but also enriches the data and transforms it into an analysis-ready form without having to write a single line of code.

Its completely automated pipeline offers data to be delivered in real-time without any loss from source to destination. Its fault-tolerant and scalable architecture ensure that the data is handled in a secure, consistent manner with zero data loss and supports different forms of data. The solutions provided are consistent and work with different BI tools as well.

Get Started with Hevo for Free

Check out why Hevo is the Best:

  • Secure: Hevo has a fault-tolerant architecture that ensures that the data is handled in a secure, consistent manner with zero data loss.
  • Schema Management: Hevo takes away the tedious task of schema management & automatically detects the schema of incoming data and maps it to the destination schema.
  • Minimal Learning: Hevo, with its simple and interactive UI, is extremely simple for new customers to work on and perform operations.
  • Connectors: Hevo supports 100+ Integrations to SaaS platforms such as WordPress, FTP/SFTP, Files, Databases, BI tools, and Native REST API & Webhooks Connectors. It supports various destinations including Google BigQuery, Amazon Redshift, Snowflake, Firebolt, Data Warehouses; Amazon S3 Data Lakes; Databricks, MySQL, SQL Server, TokuDB, MongoDB, DynamoDB, PostgreSQL Databases to name a few.  
  • Hevo Is Built To Scale: As the number of sources and the volume of your data grows, Hevo scales horizontally, handling millions of records per minute with very little latency.
  • Incremental Data Load: Hevo allows the transfer of data that has been modified in real-time. This ensures efficient utilization of bandwidth on both ends.
  • Live Support: The Hevo team is available round the clock to extend exceptional support to its customers through chat, email, and support calls.
  • Live Monitoring: Hevo allows you to monitor the data flow and check where your data is at a particular point in time.  
Sign up here for a 14-Day Free Trial!

How to Build Scalable Applications using NestJS Mongoose?

NestJS Mongoose - NestJS Mongoose Logo
Image Source

You can either use the built-in NestJS TypeORM module or the NestJS Mongoose package to connect to a MongoDB database and build scalable applications. In this article, you will learn how to create a simple “ProductCRUD(Create, Read, Update & Delete) application using NestJS Mongoose to perform the basic operations. Do Ensure that you have a MongoDB instance up & running on your system. To start using NestJS Mongoose for building applications, follow the simple steps below:

NestJS Mongoose Step 1: Installing NestJS Mongoose Package

  • Step1: Firstly, run the following command to install the basic NestJS Mongoose package and wrapper.
$ npm install --save @nestjs/mongoose mongoose
  • Step 2: To check if the installation is successful, you run the command below to see the version of the NestJS Mongoose module.
$ nest -v

NestJS Mongoose Step 2: Connecting NestJS MongoDB

Once the installation is done, you can then setup the connection your local MongoDB instance. You can do this importing the MongooseModule into the root AppModule.

import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [MongooseModule.forRoot('mongodb://localhost/demo')],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Here in the app.module.ts file, the forRoot() method is used to establish the connection in the imports section. It accepts the same configuration object as mongoose.connect(). The parameter in the forRoot method connects to the demo Database through the local MongoDB instance.

NestJS Mongoose Step 3: Model Injecttion

Now, you can define the schema. A schema decides how a MongoDB Collection will appear. Then, you can use this schema to create models that are responsible for creating, updating, and reading documents from the collection. Instead of creating a schema with Mongoose manually, you can use the NestJS Decorators. This decorator improves the code readability because of the declarative approach. 

  • Step 1: You can define the ProductSchema via the follolwing code in the product.schema.ts file:
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Document } from "mongoose";

export type Document = Product & Document;

@Schema()
export class Product {

    @Prop()
    name: string;

    @Prop()
    manufacturer: string;

    @Prop()
    manufactureYear: number;
}

export const ProductSchema = SchemaFactory.createForClass(Product);

The above code uses the following 2 NestJS Decorators:

  • @Schema: This decorator fixes the class as the schema definition. Here, it maps the class “Product” to a collection named “Products” in the demo database. Hence, the collection name is the same as the class name with an “s” at the end. Also, the @Schema Decorator only accepts a single argument i.e. schema options object. There are several schema options available.
  • @Prop: This is used to define properties within the document. In this example, these are name, manufacturer, and manufactureYear. Using Typescript’s metadata and class reflection, the types for these properties are automatically inferred.
  • Step 2: You can now add this schema to the module-level configuration. Go to the app.module.ts file and specify the presence of the schema and the model using the forFeature() method in the imports section.
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { Product, ProductSchema } from './schemas/product.schema';

@Module({
  imports: [MongooseModule.forRoot('mongodb://localhost/demo'),
            MongooseModule.forFeature([{name: Product.name, schema: ProductSchema}])],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

NestJS Mongoose Step 4: Create a Service Class

  • Step 1: Now, you can follow the code given below to create a service class that serves as a bridge between the request handlers and the MongoDB database. In this example, this service class includes the methods to create, read, update and delete a product document from the underlying products collection. You can use the standard methods available with the productModel object to perform these basic operations.
import { Injectable } from "@nestjs/common";
import { InjectModel } from "@nestjs/mongoose";
import { Model } from "mongoose";
import { Product, ProductDocument } from "src/schemas/product.schema";

@Injectable()
export class ProductService {

    constructor(@InjectModel(Product.name) private productModel: Model<ProductDocument>) {}
    
    async create(product: Product): Promise<Product> {
        const newProduct = new this.productModel(Product);
        return newProduct.save();
    }

    async readAll(): Promise<Product[]> {
        return await this.productModel.find().exec();
    }

    async readById(id): Promise<Product> {
        return await this.productModel.findById(id).exec();
    }

    async update(id, Product: Product): Promise<Product> {
        return await this.productModel.findByIdAndUpdate(id, Product, {new: true})
    }

    async delete(id): Promise<any> {
        return await this.productModel.findByIdAndRemove(id);
    }
}

The above code consists of the following 2 NestJs Decorators:

  • @Injectable: This decorator annotates the ProductService class that allows you to inject it into other classes using the principles of dependency injection.
  • @InjectModel: Thisdecorator injects the productModel inside the constructor of the class.
  • Step 2: You can add the ProductService to the app.module.ts file in the providers array.
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { Product, ProductSchema } from './schemas/product.schema';
import { ProductService } from './services/product.service';

@Module({
  imports: [MongooseModule.forRoot('mongodb://localhost/demo'),
            MongooseModule.forFeature([{name: Product.name, schema: ProductSchema}])],
  controllers: [AppController],
  providers: [AppService, ProductService],
})
export class AppModule {} 

NestJS Mongoose Step 5: Create a Controller

  • Step 1: You can create a basic controller i.e. the request handlers to perform the CRUD operations. Add the following code to the product.controller.ts file.
import { Body, Controller, Delete, Get, HttpStatus, Param, Post, Put, Res } from "@nestjs/common";
import { Product } from "src/schemas/product.schema";
import { ProductService } from "src/services/product.service";

@Controller('products')
export class ProductController {
    constructor(private readonly productService: ProductService){}

    @Post()
    async createProduct(@Res() response, @Body() product: Product) {
        const newProduct = await this.productService.create(product);
        return response.status(HttpStatus.CREATED).json({
            newProduct
        })
    }

    @Get()
    async fetchAll(@Res() response) {
        const products = await this.productService.readAll();
        return response.status(HttpStatus.OK).json({
            products
        })
    }

    @Get('/:id')
    async findById(@Res() response, @Param('id') id) {
        const product = await this.ProductService.readById(id);
        return response.status(HttpStatus.OK).json({
            product
        })
    }

    @Put('/:id')
    async update(@Res() response, @Param('id') id, @Body() product: Product) {
        const updatedProduct = await this.productService.update(id, product);
        return response.status(HttpStatus.OK).json({
            updatedProduct
        })
    }

    @Delete('/:id')
    async delete(@Res() response, @Param('id') id) {
        const deletedProduct = await this.productService.delete(id);
        return response.status(HttpStatus.OK).json({
            deletedProduct
        })
    }
}

In the above code, the ProductService is injected into the constructor. Hence, NestJS will provide a ProductService instance to the controller at runtime. Now, you can run standard POST, GET, PUT and DELETE request handlers to perform the Create, Add, Update and Delete operations respectively.

  • Step 2: Add the controller to the app.module.ts file in the controllers array to register the ProductController.
import { Module } from '@nestjs/common';
import { MongooseModule } from '@nestjs/mongoose';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ProductController } from './controllers/product.controller';
import { Product, ProductSchema } from './schemas/product.schema';
import { ProductService } from './services/product.service';

@Module({
  imports: [MongooseModule.forRoot('mongodb://localhost/demo'),
            MongooseModule.forFeature([{name: Product.name, schema: ProductSchema}])],
  controllers: [AppController, ProductController],
  providers: [AppService, ProductService],
})
export class AppModule {}

Finally, you can start the application to access the endpoints at http://localhost:3000 and perform the CRUD operations as required.

Conclusion

In this article, you have learned how to effectively build a simple scalable CRUD application using NestJS Mongoose. Owing to MongoDB’s schemaless structure, you can use it as your open-source NoSQL scalable database for your applications. Using the NestJS Mongoose package, you have easily connected to your local MongoDB instance. By creating a simple NestJS Service Class and Controller, you can start performing basic database operations such as create, read, update and delete.

As you start collecting your NestJS application data in your MongoDB database, you can begin analyzing it to get important business insights. To get a complete overview of your business performance, it is essential to consolidate data from MongoDB and all the other applications used across your firm. To achieve this you need to assign a portion of your engineering bandwidth to Integrate data from all sources, Clean & Transform it, and finally, Load it to a Cloud Data Warehouse or a destination of your choice for further Business Analytics. All of these challenges can be comfortably solved by a Cloud-based ETL tool such as Hevo Data.   

Visit our Website to Explore Hevo

Hevo Data, a No-code Data Pipeline can seamlessly transfer data from a vast sea of 100+ sources such as MongoDB & MongoDB Atlas to a Data Warehouse or a Destination of your choice to be visualized in a BI Tool. It is a reliable, completely automated, and secure service that doesn’t require you to write any code!  

If you are using MongoDB as your NoSQL Database Management System and searching for a no-fuss alternative to Manual Data Integration, then Hevo can effortlessly automate this for you. Hevo, with its strong integration with 100+ sources & BI tools(Including 40+ Free Sources), allows you to not only export & load data but also transform & enrich your data & make it analysis-ready in a jiffy.

Want to take Hevo for a ride? Sign Up for a 14-day free trial and simplify your Data Integration process. Do check out the pricing details to understand which plan fulfills all your business needs.

Tell us about your experience of creating an application using NestJS Mongoose! Share your thoughts with us in the comments section below.

Sanchit Agarwal
Former Research Analyst, Hevo Data

Sanchit Agarwal is a data analyst at heart with a passion for data, software architecture, and writing technical content. He has experience writing more than 200 articles on data integration and infrastructure. He finds joy in breaking down complex concepts in simple and easy language, especially related to data base migration techniques and challenges in data replication.

No-code Data Pipeline for MongoDB