import { EntityGenerator } from '@mikro-orm/entity-generator';
import { Migrator } from '@mikro-orm/migrations';
import { defineConfig } from '@mikro-orm/postgresql';
import { SeedManager } from '@mikro-orm/seeder';
import dotenv from 'dotenv';
dotenv.config();
export default defineConfig({
// for automatic entity loading and discovery
// so that can create relationship between entites and set up database schema
entities: ['./dist/src/entities/*.js', './dist/src/**/*.entity.js'],
entitiesTs: ['./src/entities/*.ts', './src/**/*.entity.ts'],
dbName: process.env.DATABASE_NAME,
user: process.env.DATABASE_USER,
password: process.env.DATABASE_PASSWORD,
host: process.env.DATABASE_HOST,
port: process.env.DATABASE_PORT
? parseInt(process.env.DATABASE_PORT, 10)
: 5432,
seeder: {
path: './seeders', // path to the folder with seeders
pathTs: undefined, // path to the folder with TS seeders (if used, you should put path to compiled files in `path`)
defaultSeeder: 'DatabaseSeeder', // default seeder class name
glob: '!(*.d).{js,ts}', // how to match seeder files (all .js and .ts files, but not .d.ts)
emit: 'ts', // seeder generation mode
fileName: (className: string) => className, // seeder file naming convention
},
// Install the add-on
extensions: [EntityGenerator, Migrator, SeedManager],
// allowGlobalContext: true,
});
NestJS Setting
Declare in root level
app.module.ts
import { Module } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { MikroOrmModule } from '@mikro-orm/nestjs';
@Module({
imports: [MikroOrmModule.forRoot()],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
To define which repositories should be registered in the current scope you can use the forFeature() method.
The Entity Manager is a core component in MikroORM that handles all database operations and entity state management. Here's a comprehensive explanation:
Core Responsibilities:
Managing entity lifecycle
Handling database transactions
Persisting entities to the database
Managing entity relationships
Handling unit of work pattern
em.flush() will go through all managed entities, compute appropriate change sets and perform according database queries. As an entity loaded from database becomes managed automatically, we do not have to call persist on those, and flush is enough to update them.
const book = await em.findOne(Book, 1);
book.title = 'How to persist things...';
// no need to persist `book` as its already managed by the EM
await em.flush();
To save entity state to database, we need to persist it. Persist determines whether to use insert or update and computes appropriate change-set. Entity references that are not persisted yet (does not have identifier) will be cascade persisted automatically.
// use constructors in our entities for required parameters
const author = new Author('Jon Snow', 'snow@wall.st');
author.born = new Date();
const publisher = new Publisher('7K publisher');
const book1 = new Book('My Life on The Wall, part 1', author);
book1.publisher = publisher;
const book2 = new Book('My Life on The Wall, part 2', author);
book2.publisher = publisher;
const book3 = new Book('My Life on The Wall, part 3', author);
book3.publisher = publisher;
// just persist books, author and publisher will be automatically cascade persisted
await em.persist([book1, book2, book3]).flush();
// or one by one
em.persist(book1);
em.persist(book2);
em.persist(book3);
await em.flush(); // flush everything to database at once
Entity Repositories are thin layers on top of EntityManager. They act as an extension point, so you can add custom methods, or even alter the existing ones. The default EntityRepository implementation just forwards the calls to underlying EntityManager instance.
profile.repo.ts
import { EntityRepository } from '@mikro-orm/postgresql';
import { Profile } from './profile.entity';
export class ProfileRepository extends EntityRepository<Profile> {}
const em = orm.em.fork();
await em.begin();
try {
//... do some work
const user = new User(...);
user.name = 'George';
em.persist(user);
await em.commit(); // will flush before making the actual commit query
} catch (e) {
await em.rollback();
throw e;
}
Unit of Work
MikroORM uses the Identity Map pattern to track objects. Whenever you fetch an object from the database, MikroORM will keep a reference to this object inside its UnitOfWork.
Only one SELECT query will be fired against the database here. In the second findOne() call MikroORM will check the identity map first and will skip the database round trip as it will find the entity already loaded.
It starts off empty, gets filled and updated as you perform calls with the entity manager, and items in it get pulled out of it when an operation matches an ID the identity map is aware of
It helps to reduce your application's memory footprint per request, by ensuring that even if you make multiple queries that match the same rows, those rows will only exist once in memory.
Migration
It allows you to generate migrations based on the current schema difference ( which is based on the entity discovery)
Migrations are classes that extend Migration abstract class:
npx mikro-orm migration:create # Create new migration with current schema diff
npx mikro-orm migration:up
# For forward change for executing up function
# Migrate up to the latest version, new record will be inserted into migration table
npx mikro-orm migration:down
# For reverse change for executing down function
# Migrate one step down, the record will be removed from migration table
npx mikro-orm migration:list # List all executed migrations
npx mikro-orm migration:check # Check if schema is up to date
npx mikro-orm migration:pending # List all pending migrations
npx mikro-orm migration:fresh
# Drop the database and migrate up to the latest version
# The whole of thing restart again, not suitable for production database
Seeding
To initialize the data into table
import { EntityManager } from '@mikro-orm/core';
import { Seeder } from '@mikro-orm/seeder';
import { Author } from './author'
export class DatabaseSeeder extends Seeder {
async run(em: EntityManager): Promise<void> {
// will get persisted automatically
const author = em.create(Author, {
name: 'John Snow',
email: 'snow@wall.st'
});
// but if you would do `const author = new Author()` instead,
// you would need to call `em.persist(author)` explicitly.
}
}
npx mikro-orm seeder:run -c DatabaseSeeder
Entity Generator
To generate entities from existing database schema
Code first: write the entity definitions first and then generate db schema by using migration
Schema first: write the schema definition first (or in the case of migrations, write the migrations first), execute it, and generate the entity definitions out of the database