Integrating MongoDB in your NestJS Project: A Quick Guide
In my current project, I am building an authentication system using NestJS. Got to the part of integrating MongoDB in the project, I thought it would be helpful to create a quick guide outlining the steps involved.
Prerequisite
To follow this tutorial through, I am assuming you are familiar with
Setting up a NestJS project
Creating and setting up your database on MongoDB Atlas
Installing the dependencies
At this point, you should have set up your NestJS project using the command nest new my-project
and also created your Mongodb database. Also, you should have your MongoDB Connection URL ready. Refer here to create your Mongo database. Install these two packages:
npm i @nestjs/mongoose mongoose
or
yarn add @nestjs/mongoose mongoose
Creating User Model/Schema
Since the example that is being used here is an authentication system, we can create the schema around User. Create a new folder named User where you have your module, controller, service and schema files or you can use the command nest g resource user
to generate it.
In the user.schema.ts file add the following code block
import { Prop, Schema, SchemaFactory } from '@nestjs/mongoose';
export type UserDocument = User & Document;
@Schema()
export class User {
@Prop({
unique: true,
required: true,
})
email: string;
@Prop({
required: true,
})
password: string;
@Prop({
required: true,
})
name: string;
}
export const UserSchema = SchemaFactory.createForClass(User);
Here we are defining the schema for the application in Nest. The User class represents our schema and as you can see it is decorated with the @Schema decorator. The @Prop decorator is used to define the properties of the fields (name, password, email). The method SchemaFactory.createForClass takes in the user as an argument to generate Mongoose schema based on the class we have defined earlier.
Create User Module/Controller/Service
As previously mentioned, you should have your user.controller.ts
and user.service.ts
In the user.module.ts file, add the UserController and UserService to the controllers and providers prop respectively.
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from './user.schema';
@Module({
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
Next, add the import prop to import the dependencies required by the module which in this case is the MongooseModule. The Mongoose.forFeature() method will be provided with an array of objects that specifies the Mongoose models to be registered with this module (User and UserSchema).
import { Module } from '@nestjs/common';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { MongooseModule } from '@nestjs/mongoose';
import { User, UserSchema } from './user.schema';
@Module({
imports: [
MongooseModule.forFeature([
{
name: User.name,
schema: UserSchema,
},
]),
],
controllers: [UserController],
providers: [UserService],
})
export class UserModule {}
In the user.controller.ts, we can create a simple signup route that takes in the fields defined in the schema earlier.
Your user.dto.ts file should look like this:
export class SignupDTO {
email: string;
password: string;
name: string;
}
import { Body, Controller, Post } from '@nestjs/common';
import { UserService } from './user.service';
import { SignupDTO } from './user.dto';
@Controller('auth')
export class UserController {
constructor(private readonly userService: UserService) {}
@Post('/signup')
async signUp(@Body() signupDTO: SignupDTO): Promise<SignupDTO> {
const { email, password, name } = signupDTO;
const user = await this.userService.signUp(email, password, name);
return user;
}
}
Here, the req body is extracted using the @Body decorator and it is bound to the signupDTO. The values extracted are destructured from the signupDTO object which is then passed to the signup function in the userService.
In the user.service.ts
file:
@Injectable()
export class UserService {
constructor(
@InjectModel(User.name) private readonly userModel: Model<User>,
) {}
async signUp(
email: string,
password: string,
name: string,
): Promise<SignupDTO> {
const newUser = new this.userModel({ email, password, name });
const user = await newUser.save();
return user;
}
}
Here, the InjectorModel is used to inject the User model from the Mongoose schema that was earlier defined. Now we can interact with the database. The signup method takes in the 3 parameters that were passed into it. A new instance of the userModel is created using the provided data (name, email, password). The save() method is called to save the newly created user to the MongoDB database created.
Lastly, in your app.module.ts
@Module({
imports: [
ConfigModule.forRoot({
envFilePath: '.env',
isGlobal: true,
}),
MongooseModule.forRoot(process.env.MONGO_URI),
UserModule,
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Ensure the UserModule is being imported and added to the import props of the AppModule. The MongooseModule.forRoot is used to configure and connect the MongoDB database. The process.env.MONGO_URI
is an env variable that should contain the URI of your database.
In your .env
file you should have the MONGO_URI defined:
PORT=8000
MONGO_URI=mongodb+srv://xxxxxxxxxxx
To test the auth/signup
endpoint in Postman, you can send a POST request to http://localhost:3000/auth/signup
(assuming your NestJS server is running on port 3000). Include the user details (email, password, and name) in the request body as a JSON payload. After sending the request, you should receive a successful response containing the user object. To confirm that the new user document has been created, you can refresh your MongoDB Atlas dashboard and verify the presence of a document with the same user details as returned in the Postman response.