Create an application for several leases in Nest.js – part 2

I Create a multi-tenancy application In Nest.js – Part 1 we set up the Nest.js framework, configured and tested the microservice application using Nest.js.

Database

Nest gives us all the tools to work with any SQL and NoSQL database. You have many options, you can also use almost all ORMs and libraries in Nest.js and typescript, such as Sequelize, TypeORM, Prisma and of course mongoose.

In this application we will work with MySQL and MongoDB. We will also use the popular Js libraries including Sequelize as ORM for MySQL and mongoose for MongoDB.

Database integration

To start using Sequelize, we first need to install the necessary dependencies, which include @ nestjs / Sequelize, MySQL2, because we want to connect to the MySQL database and other necessary dependencies.

$ npm install --save @nestjs/sequelize sequelize sequelize-typescript mysql2
$ npm install --save-dev @types/sequelize

In the services, we import SequelizeModule in the main modules to set the connection configuration:

ex: app.modul.ts

@Module({
  imports: [
    SequelizeModule.forRoot({
      dialect: 'mysql',
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'root',
      database: 'test',
      models: [],
    }),
  ],
})

That forRoot() method will include all configuration properties. You can read more details here.

After configuring the connection, we need to create a table entity. For example, we can set a user model in the user service (will also add the connection in the service) by creating user.model.ts, which will look like this:

brugermodel.ts

/// imports
@Table({tableName:'Users'})
export class Users extends Model<Users> {
    @Column( {allowNull: false })
    firstName: string;

    @Column( {allowNull: false })
    lastName: string;

    @Column( { allowNull: false,unique: true })
    email: string;

    @Column( {allowNull: false})
    password: string;    
      
    @Column( { allowNull: false})
    type: string;
}

We should also add dto:

create-user-dto.ts

export class CreateUserDto{
    readonly firstName:string
    readonly lastName:string
   readonly   email:string
   readonly password:string
   readonly type:string
}

And do not forget to add users in the model array forRoot()

Let us now complete the setup and configuration. If you do not have a database, create an empty table and change the Sequelize configuration by adding: autoLoadModels: true, synchronize: true . So in the module you will add the repository by adding SequelizeModule.forFeature([Users]) in the import array. In our case, we use the main module so that it becomes:

user-service.modul.ts

@Module({
  imports: [SequelizeModule.forRoot({
    dialect: 'mysql',
    host: 'localhost',
    port: 3306,
    username: 'ismaeil',
    password: 'root',
    database: 'test',
    autoLoadModels: true,
    synchronize: true,
    models: [Users],
  }),SequelizeModule.forFeature([Users])],
  controllers: [UserServiceController],
  providers: [UserServiceService],
})

And we will edit the main service to add findAll and create method:

user-service.service.ts

@Injectable()
export class UserServiceService {
  constructor(
    @InjectModel(Users)
  private readonly userModel: typeof Users){}
  async findAll(): Promise<Users[]> {
    return this.userModel.findAll() ;
  }
  
  async create( createUserDto:CreateUserDto):Promise<Users> {
    return this.userModel.create(<Users>createUserDto)
  }
}

Finally, you need to edit the controller to enable the use of REST requests to access and edit the database:

user-service.controller.ts

@Controller('users')
export class UserServiceController {
  constructor(private readonly userServiceService: UserServiceService) {}

  @Get()
  async findAll(){
      return this.userServiceService.findAll();
  }

  @Post()
  async createUser(@Body() createUserDto:CreateUserDto){
    return  this.userServiceService.create(createUserDto)
  }

}

Now run the browser and test http://127.0.0.1:3003/users. This should access the database and create a table for the first time and return an empty array. We can add data using a POST request:

Additional tips

If we have an existing database and need to import tables with types without much work, we can use sequelize-typescript generator.

You can apply for it to see how it works, but here are some simple steps:

  1. Download and install npx
  2. Create a folder to store output typescript models mkdir models
  3. Install sequelize-typescript generator on your machine: npm install -g sequelize-typescript-generator
  4. Install mysql driver: npm install -g mysql2
  5. Run the command: npx stg -D mysql -h localhost -p 3306 -d <databaseName> -u <username> -x <password> --indices --case camel --out-dir models --clean

Source code available in the Git branch database connection.

Like the previous one, we need to install dependencies to use MongoDB in Nest:

$ npm install --save @nestjs/mongoose mongoose

Import MongooseModule to the root module

@Module({
  imports: [MongooseModule.forRoot('mongodb://localhost:27017/test')],
})

forRoot() accepts the same configuration as mongoose.connect () from the Mongoose package.

We will use the MongoDB database in the notification service. First we will add forRoot() in the root module and will create a child module called a message to serve message messages.

The root module will look like this:

notification module.ts

@Module({
  imports: [MongooseModule.forRoot('mongodb://localhost:27017/test'),
  MessageModule],
  controllers: [NotificationController],
  providers: [NotificationService],
})

Because we use mongoose, we need to create a schema and then import the repository into a module.

In src / message / schemes we will create message.schema.ts file which will look like this:

export type MessageSchemaDocument = MessageSchema & Document;

@Schema()
export class MessageSchema{
    @Prop()
    name: string    
    
    @Prop()
    createdAt: Date

    @Prop({type:mongoose.Schema.Types.Mixed})
    data: Record<string, any>
}

export const MessageSchemaSchema = SchemaFactory.createForClass(MessageSchema);

Add the following code in message.module:

besked.modul.ts

@Module({
  imports: [MongooseModule.forFeature([{name:MessageSchema.name,schema:MessageSchemaSchema}])],
  controllers: [MessageController],
  providers: [MessageService],
})

And add the following methods in the messaging service:

besked.service.ts

@Injectable()
export class MessageService {
    constructor(@InjectModel(MessageSchema.name) private readonly messageModel: Model<MessageSchemaDocument>) {}
    async findAll () {
        return await this.messageModel.find().exec()
    }    
    async create (messageDto:MessageDto) {
        return await this.messageModel.create(messageDto)
    }
}

To use it as a reply body type we can create MessageDto:

export class MessageDto {
    readonly name: string    
    readonly createdAt:Date = new Date();
    readonly data?: any
}

For mapping request:

message.controller.ts

@Controller('message')
export class MessageController {
  constructor(private readonly messagenService: MessageService) {}

  @Get()
  async findAll(){
    return this.messagenService.findAll();
  }

  @Post()
  @UsePipes(new ValidationPipe({ transform: true }))
  async create(@Body() messageDto:MessageDto){
    return this.messagenService.create(messageDto);
  }
}

Note: Tubes are used to transform and validate input data, in our case we can use @UsePipes(new ValidationPipe({ transform: true })) to set the blank properties of Dto with default values. For more details refer to Pipes and Validation.

You can now test using a Post Request for the URL http://127.0.0.1:3002/message with body:

{
     "name":"validation",
        "data":{"message":"testing validation message if it success","status":"valid"}
    }

To retrieve all the records, use Get request http://127.0.0.1:3002/message and the source code for this available in the Git branch mongodb connection.

So that was it for now! In Part 3, we will complete the database setup to use multiple databases depending on the request header.

.

Leave a Comment