MikroORM is a Typescript-based ORM for NodeJS environment. Due to its Typescript affinity, it works well with NestJS. In this post, we will learn how to create a MikroORM NestJS application.

To create a NestJS MikroORM application, we need to use the third-party package @mikro-orm/nestjs. Using this package, we can use Mikro ORM to connect to various databases such as PostgreSQL, MySQL, SQLite and even MongoDB.

1 – Is Mikro ORM good?

Mikro ORM is pretty good. It comes with a great type-safety system and an auto-flush mode. MikroORM works on the principle of unit of work and identity map pattern. This allows MikroORM to optimize data-fetching and prevent unnecessary round trips to the database.

Some of the key features of MikroORM include implicit transactions, identity mapping, automatic refresh of loaded entities and so on. With MikroORM, you get a fully-featured ORM for almost all the major database options.

Due to its robust features, MikroORM is a great option to use with NestJS.

By the way, if you are interested in backend frameworks and concepts, you’d love the Progressive Coder Newsletter where I talk about such topics in a fun & interesting approach.

Subscribe now and join along.

2 – Installation of NestJS MikroORM Packages

The first step to build a MikroORM NestJS application is to install the necessary packages.

We can do by executing the below command.

$ npm i @mikro-orm/core @mikro-orm/nestjs @mikro-orm/mysql @mikro-orm/cli

Note here that the @mikro-orm/nestjs is a third-party package. In other words, it is not part of the core NestJS framework.

Since we want to work with MySQL, we also install the @mikro-orm/mysql package. Basically, there are different drivers for different database solutions. Depending on your database of choice, you need to use the appropriate driver package.

The @mikro-orm/cli package will be used while configuring the NestJS MikroORM connection in the next section.

3 – Configuring the NestJS MikroORM MySQL Connection

Once the packages are successfully installed, we need to configure the NestJS MikroORM connection. In other words, we need to provide the connection parameters for our database.

This can be done in the App module. See below:

import { MikroOrmModule } from '@mikro-orm/nestjs';
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { LibraryModule } from './library/library.module';

@Module({
  imports: [
    MikroOrmModule.forRoot(), 
    LibraryModule
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

The MikroOrmModule.forRoot() function takes a configuration object as input. This configuration object is same as the one we use for Mikro ORM Init function.

We can use this object to configure the database connection. However, if we don’t provide any input object, MikroORM CLI looks for a file named mikro-orm.config.ts in the project’s src directory.

See below:

import { Logger } from '@nestjs/common';
import { Options } from '@mikro-orm/core';
import { MikroBook } from './library/book.entity';

const logger = new Logger('MikroORM');
const config: Options = {
  entities: [MikroBook],
  dbName: 'testdb',
  type: 'mysql',
  port: 3306,
  debug: true,
  user: 'root',
  password: 'password',
  logger: logger.log.bind(logger),
};

export default config;

The properties within the configuration options are self-explanatory. Basically, these properties help us connect to the MySQL instance running on port 3306. The type property needs to change in case we are using a different database.

To make this setup work, we also need to add some configuration to our package.json file as below:

"mikro-orm": {
    "useTsNode": true,
    "configPaths": [
      "./src/mikro-orm.config.ts",
      "./dist/mikro-orm.config.js"
    ]
  }

Basically, we are letting NestJS and MikroORM know where to find our configuration file.

Note that we are yet to create the entity MikroBook. However, we have already mentioned it in the entities array. Also, we have imported the Library module in the App Module. We will be creating them in the next section.

4 – Creating MikroORM Entity

For our demo application, we will create a folder named library within our src directory. This will be the location of the Library module.

Next, we create a MikroORM entity as below:

import { Entity, PrimaryKey, Property } from "@mikro-orm/core";

@Entity()
export class MikroBook {
    
    @PrimaryKey()
    id: number;

    @Property()
    title: string;

    @Property()
    author: string;

    constructor(title: string, author: string) {
        this.title = title;
        this.author = author;
    }
}

Here, we are defining the structure of our database table. We use @Entity() decorator to make MikroORM aware of this entity. For the field level, we use special decorators such as @PrimaryKey() and @Property(). These decorators are part of the @mikro-orm/core package.

5 – Using MikroORM Repository in NestJS Service

MikroORM follows the repository pattern. It means that every entity has its own repository interface. Using the repository, we can perform CRUD operations on the underlying table.

We can inject the repository for the entity MikroBook within a typical NestJS Service as below:

import { EntityRepository } from "@mikro-orm/core";
import { InjectRepository } from "@mikro-orm/nestjs";
import { MikroBook } from "./book.entity";
import { CreateBookDTO } from "./dto/create-book-dto";

@Injectable()
export class BookService {
    constructor(
        @InjectRepository(MikroBook)
        private readonly bookRepository: EntityRepository<MikroBook>
    ) {}

    async fetchAll(): Promise<MikroBook[]> {
        return this.bookRepository.findAll();
    }

    async findOne(id: number): Promise<MikroBook> {
        const findOneOptions = {
            id: id
        }
        return this.bookRepository.findOne(findOneOptions);
    } 

    async createBook(dto: CreateBookDTO): Promise<MikroBook> {

        const { title, author } = dto
        const book = new MikroBook(title, author)

        await this.bookRepository.persistAndFlush(book);
        return book;
    }
}

We have implemented a few operations to read records and create a new book record. Within the constructor of the BookService, we inject the repository for MikroBook using EntityRepository.

To make this work, we also need to configure the repository in the Library module by specifying the list of entities in the forFeature() function. In our case, we have only entity i.e. MikroBook.

import { MikroOrmModule } from "@mikro-orm/nestjs";
import { Module } from "@nestjs/common";
import { BookController } from "./book.controller";
import { MikroBook } from "./book.entity";
import { BookService } from "./book.service";

@Module({
    imports: [MikroOrmModule.forFeature({ entities: [MikroBook]})],
    controllers: [BookController],
    providers: [BookService]
})
export class LibraryModule{}

We also add the BookService to the providers array so that we can inject it in the BookController.

6 – Creating the NestJS Controller

Last step is to create a NestJS Controller to interface with the application service.

See below:

import { Body, Controller, Get, Post } from "@nestjs/common";
import { MikroBook } from "./book.entity";
import { BookService } from "./book.service";
import { CreateBookDTO } from "./dto/create-book-dto";

@Controller('books')
export class BookController {
    constructor(private readonly bookService: BookService) {}

    @Get()
    async fetchBooks(): Promise<MikroBook[]> {
        return this.bookService.fetchAll()
    }

    @Post()
    async createBook(@Body() dto: CreateBookDTO): Promise<MikroBook> {
        console.log(dto)
        return this.bookService.createBook(dto);
    }
}

Here, we are simply injecting the BookService and using the appropriate methods to create a couple of request handlers.

For creating a book, we have declared a special interface CreateBookDTO as below:

export interface CreateBookDTO {
    title: string;
    author: string;
}

We can now start the application using npm run start and access it on http://localhost:3000. In case the database and tables are not already present, the MikroORM driver will automatically create them for us.

Conclusion

NestJS MikroORM integration is pretty straightforward once the initial configuration is complete. By using the Repository pattern, it becomes very easy to perform various operations on the individual entities.

The code for this post is available on Github.

However, MikroORM is not the only ORM available with NestJS. In case you are interested in other options, you can check out NestJS TypeORM and NestJS Sequelize. Also, we can use Prisma to build NestJS REST APIs.

If you have any comments or queries about this post, please feel free to mention them in the comments section below.

Anyways, before we end this post, a quick reminder about the Progressive Code Newsletter where I explain backend frameworks and concepts in a fun & interesting manner so that you never forget what you’ve learned.

I’m 100% sure you’d love it.

Subscribe now and see you over there.

Categories: BlogNestJS

Saurabh Dashora

Saurabh is a Software Architect with over 12 years of experience. He has worked on large-scale distributed systems across various domains and organizations. He is also a passionate Technical Writer and loves sharing knowledge in the community.

0 Comments

Leave a Reply

Your email address will not be published. Required fields are marked *