Integrating and Using Databases in NestJS

This article provides a detailed explanation of how to integrate and use relational and non-relational databases in a NestJS project, including database configuration, model design, and the implementation of complex queries.

Integrating Relational Databases

Configuring TypeORM

TypeORM is a powerful ORM tool that supports various relational databases. We will use MySQL as an example to demonstrate how to configure TypeORM.

  1. Install the necessary packages:
Terminal window
1
npm install --save @nestjs/typeorm typeorm mysql2
  1. Configure TypeORM in app.module.ts:
1
import { Module } from "@nestjs/common";
2
import { TypeOrmModule } from "@nestjs/typeorm";
3
import { UsersModule } from "./users/users.module";
4
5
@Module({
6
imports: [
7
TypeOrmModule.forRoot({
8
type: "mysql",
9
host: "localhost",
10
port: 3306,
11
username: "root",
12
password: "password",
13
database: "test",
14
entities: [__dirname + "/**/*.entity{.ts,.js}"],
15
synchronize: true,
16
}),
17
UsersModule,
18
],
19
})
20
export class AppModule {}

Designing Database Models

Create a User entity to define the database model for users:

1
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
2
3
@Entity()
4
export class User {
5
@PrimaryGeneratedColumn()
6
id: number;
7
8
@Column()
9
name: string;
10
11
@Column()
12
email: string;
13
14
@Column()
15
password: string;
16
}

Performing Database Operations

Implement CRUD operations in users.service.ts:

1
import { Injectable } from "@nestjs/common";
2
import { InjectRepository } from "@nestjs/typeorm";
3
import { Repository } from "typeorm";
4
import { User } from "./user.entity";
5
6
@Injectable()
7
export class UsersService {
8
constructor(
9
@InjectRepository(User)
10
private usersRepository: Repository<User>,
11
) {}
12
13
findAll(): Promise<User[]> {
14
return this.usersRepository.find();
15
}
16
17
findOne(id: number): Promise<User> {
18
return this.usersRepository.findOne(id);
19
}
20
21
create(user: User): Promise<User> {
22
return this.usersRepository.save(user);
23
}
24
25
async update(id: number, user: Partial<User>): Promise<void> {
26
await this.usersRepository.update(id, user);
27
}
28
29
async remove(id: number): Promise<void> {
30
await this.usersRepository.delete(id);
31
}
32
}

Implementing Complex Queries

Implement complex queries in users.service.ts:

1
import { Injectable } from "@nestjs/common";
2
import { InjectRepository } from "@nestjs/typeorm";
3
import { Repository } from "typeorm";
4
import { User } from "./user.entity";
5
6
@Injectable()
7
export class UsersService {
8
constructor(
9
@InjectRepository(User)
10
private usersRepository: Repository<User>,
11
) {}
12
13
// Complex query example: Find users with a specific keyword
14
async findUsersByKeyword(keyword: string): Promise<User[]> {
15
return this.usersRepository
16
.createQueryBuilder("user")
17
.where("user.name LIKE :keyword OR user.email LIKE :keyword", {
18
keyword: `%${keyword}%`,
19
})
20
.getMany();
21
}
22
}

Integrating Non-Relational Databases

Configuring Mongoose

Mongoose is a popular ODM tool for MongoDB. We will demonstrate how to configure Mongoose.

  1. Install the necessary packages:
Terminal window
1
npm install --save @nestjs/mongoose mongoose
  1. Configure Mongoose in app.module.ts:
1
import { Module } from "@nestjs/common";
2
import { MongooseModule } from "@nestjs/mongoose";
3
import { UsersModule } from "./users/users.module";
4
5
@Module({
6
imports: [MongooseModule.forRoot("mongodb://localhost/nest"), UsersModule],
7
})
8
export class AppModule {}

Designing Database Models

Create a User model to define the database schema for users:

1
import { Schema } from "mongoose";
2
3
export const UserSchema = new Schema({
4
name: String,
5
email: String,
6
password: String,
7
});

Performing Database Operations

Implement CRUD operations in users.service.ts:

1
import { Injectable } from "@nestjs/common";
2
import { InjectModel } from "@nestjs/mongoose";
3
import { Model } from "mongoose";
4
import { User } from "./user.interface";
5
import { CreateUserDto } from "./create-user.dto";
6
7
@Injectable()
8
export class UsersService {
9
constructor(@InjectModel("User") private readonly userModel: Model<User>) {}
10
11
async findAll(): Promise<User[]> {
12
return this.userModel.find().exec();
13
}
14
15
async findOne(id: string): Promise<User> {
16
return this.userModel.findById(id).exec();
17
}
18
19
async create(createUserDto: CreateUserDto): Promise<User> {
20
const createdUser = new this.userModel(createUserDto);
21
return createdUser.save();
22
}
23
24
async update(id: string, user: Partial<User>): Promise<User> {
25
return this.userModel.findByIdAndUpdate(id, user, { new: true }).exec();
26
}
27
28
async remove(id: string): Promise<User> {
29
return this.userModel.findByIdAndRemove(id).exec();
30
}
31
}

Implementing Complex Queries

Implement complex queries in users.service.ts:

1
import { Injectable } from "@nestjs.common";
2
import { InjectModel } from "@nestjs/mongoose";
3
import { Model } from "mongoose";
4
import { User } from "./user.interface";
5
6
@Injectable()
7
export class UsersService {
8
constructor(@InjectModel("User") private readonly userModel: Model<User>) {}
9
10
// Complex query example: Find users with a specific keyword
11
async findUsersByKeyword(keyword: string): Promise<User[]> {
12
return this.userModel
13
.find({
14
$or: [
15
{ name: new RegExp(keyword, "i") },
16
{ email: new RegExp(keyword, "i") },
17
],
18
})
19
.exec();
20
}
21
}

Conclusion

This article provided a detailed explanation of how to integrate and use relational and non-relational databases in NestJS. Through specific configurations and code examples, we demonstrated how to use TypeORM and Mongoose for database operations, model design, and implementing complex queries. In actual development, choosing the right database and ORM/ODM tools is crucial for building efficient and scalable applications.