- Published on
NestJS and MongoDB: A Step-by-Step Integration Guide
- Authors
- Name
Introduction
Greetings, fellow developers! š Venturing into the vast universe of backend development, we often find ourselves at the crossroads of choosing the right tools. Today, Iām here to guide you through one such powerful combination: NestJS and MongoDB. Weāll embark on a journey to seamlessly integrate MongoDB into a NestJS application. Whether youāre a seasoned developer or just getting your feet wet, this guide promises clarity and actionable steps. So, grab your favorite beverage, and letās dive right in!
Prerequisites
- Basic understanding of TypeScript.
- Node.js installed.
- MongoDB running locally or a connection string to a cloud instance.
- NestJS CLI installed. If not, run:
npm i -g @nestjs/cli
1. Setting Up a New NestJS Project
Kick things off by creating a new NestJS project:
nest new nest-mongodb
Navigate to your project:
cd nest-mongodb
2. Installing Dependencies
For MongoDB, weāll use Mongoose, an elegant ODM (Object Document Mapper):
npm install mongoose
3. Configuring MongoDB in AppModule
Open app.module.ts
and set up the MongooseModule
:
import { Module } from "@nestjs/common";
import { MongooseModule } from "@nestjs/mongoose";
@Module({
imports: [MongooseModule.forRoot(process.env.MONGO_CONNECTION_STRING)],
// ... other properties
})
export class AppModule {}
Note: Always use environment variables for sensitive data like connection strings. Consider using packages like _dotenv_
for this.
4. Creating the User Module
Generate a new resource for the user:
nest generate resource module/user
Now, letās define our User
entity:
// user.entity.ts
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Document } from "mongoose";
import { v4 as uuid } from "uuid";
import { Type } from "class-transformer";
import { Client } from "src/modules/client/entities/client.entity";
export type UserDocument = User & Document;
@Schema({
toJSON: {
getters: true,
virtuals: true,
},
timestamps: true,
})
export class User {
@Prop({
type: String,
unique: true,
default: function genUUID() {
return uuid();
},
})
userId: string;
@Prop({ required: true })
firstName: string;
@Prop({ required: true })
lastName: string;
@Prop({ required: true, unique: true })
email: string;
@Prop({ required: false })
clientId?: string;
@Type(() => Client)
Client: Client;
@Prop({ required: true })
password: string;
}
export const UserSchema = SchemaFactory.createForClass(User);
UserSchema.virtual("Client", {
ref: Client.name,
localField: "clientId",
foreignField: "clientId",
justOne: true,
});
5. Creating the Client Module
Similarly, generate a resource for the client:
nest generate resource module/client
Define the Client
entity:
// client.entity.ts
import { Prop, Schema, SchemaFactory } from "@nestjs/mongoose";
import { Document } from "mongoose";
import { v4 as uuid } from "uuid";
import { Client } from "src/modules/user/entities/user.entity";
export type ClientDocument = Client & Document;
@Schema({
toJSON: {
getters: true,
virtuals: true,
},
timestamps: true,
})
export class Client {
@Prop({ required: true })
name: string;
@Prop({
required: true,
unique: true,
default: function genUUID() {
return uuid();
},
})
clientId: string;
}
export const ClientSchema = SchemaFactory.createForClass(Client);
ClientSchema.virtual("Users", {
ref: User.name,
localField: "clientId",
foreignField: "clientId",
justOne: false,
});
6. Centralizing Mongoose Features
To avoid redundancy and make our codebase cleaner, letās centralize our Mongoose features:
// src/db/for-feature.db.ts
import { User, UserSchema } from "src/modules/users/entities/user.entity";
import {
Client,
ClientSchema,
} from "src/modules/clients/entities/client.entity";
export default [
{ name: User.name, schema: UserSchema },
{ name: Client.name, schema: ClientSchema },
];
7. Integrating Mongoose Features in Modules
For both client.module.ts
and user.module.ts
, import the centralized Mongoose features:
// client.module.ts
import { Module } from "@nestjs/common";
import { MongooseModule } from "@nestjs/mongoose";
import forFeatureDb from "src/db/for-feature.db";
import { ClientController } from "./client.controller";
import { ClientService } from "./client.service";
@Module({
controllers: [ClientController],
providers: [ClientService],
imports: [MongooseModule.forFeature(forFeatureDb)],
exports: [ClientService],
})
export class ClientModule {}
// user.module.ts
import { Module } from "@nestjs/common";
import { MongooseModule } from "@nestjs/mongoose";
import forFeatureDb from "src/db/for-feature.db";
import { UserController } from "./user.controller";
import { UserService } from "./user.service";
@Module({
controllers: [UserController],
providers: [UsersService],
imports: [MongooseModule.forFeature(forFeatureDb)],
exports: [UserService],
})
export class UserModule {}
8. Building the User and Client Services
In the service files, weāll handle our CRUD operations. Hereās how you can set up the UserService
:
// user.service.ts
import { Injectable } from '@nestjs/common';
import { CreateUserDto } from './dto/create-user.dto';
import { User, UserDocument } from './entities/user.entity';
import { InjectModel } from '@nestjs/mongoose';
import { Model } from 'mongoose';
@Injectable()
export class UserService {
constructor(@InjectModel(User.name) private readonly userModel: Model<UserDocument>) {}
create(createUserDto: CreateUserDto) {
return this.userModel.create({ ...createUserDto });
}
// ... other CRUD operations
}
```Similarly, set up the `ClientService`.
## **9. Setting Up Controllers**
Controllers handle incoming HTTP requests. For the `UserController`:
```ts
// user.controller.ts
import { Controller, Post, Body, UseGuards } from '@nestjs/common';
import { UserService } from './user.service';
import { CreateUserDto } from './dto/create-user.dto';
@Controller('user')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post()
create(@Body() createUserDto: CreateUserDto) {
return this.userService.create(createUserDto);
}
// ... other endpoints
}
Repeat the process for the ClientController
.
10. Relationships in MongoDB
The virtuals in our schemas allow us to create relationships between collections. This isnāt native to MongoDB but is a powerful feature provided by Mongoose. In our example, a User
can be linked to a Client
, and vice versa.
For example, to retrieve data related to a User and their associated Client, you can use the following code:
this.userModel.find().populate('Client').lean();
This code utilizes the power of Mongooseās virtuals to establish connections between collections.
For more information about virtuals, you can refer to https://mongoosejs.com/docs/tutorials/virtuals.html
Conclusion
Congratulations! Youāve just set up a NestJS application integrated with MongoDB. This guide aimed to provide a beginner-friendly approach, but always remember that the journey of mastering any technology requires continuous learning and practice. Dive deeper and explore more features.
For more insights and discussions, feel free to visit my Medium Blog.
Happy coding! š