MikroORM
TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity ...
README
Heavily inspired by Doctrine and Nextras Orm.
🤔 Unit of What?
Unit of Work maintains a list of objects (_entities_) affected by a business transaction
and coordinates the writing out of changes. (Martin Fowler)
Identity Map ensures that each object (_entity_) gets loaded only once by keeping every
loaded object in a map. Looks up objects using the map when referring to them.
Implicit Transactions
You can also control the transaction boundaries manually via em.transactional(cb).
- ```typescript
- const user = await em.findOneOrFail(User, 1);
- user.email = 'foo@bar.com';
- const car = new Car();
- user.cars.add(car);
- // thanks to bi-directional cascading we only need to persist user entity
- // flushing will create a transaction, insert new car and update user with new email
- // as user entity is managed, calling flush() is enough
- await em.flush();
- ```
ChangeSet based persistence
- ```typescript
- @Entity()
- export class User {
- @PrimaryKey()
- id!: number;
- @Property()
- name!: string;
- @OneToOne()
- address?: Address;
- @ManyToMany()
- cars = new Collection<Car>(this);
- constructor(name: string) {
- this.name = name;
- }
- }
- ```
- ```typescript
- const user = new User('John Doe'); // name is required to create new user instance
- user.address = new Address('10 Downing Street'); // address is optional
- ```
- ```typescript
- const user = await em.findOneOrFail(User, 1, ['cars', 'address']);
- user.title = 'Mr.';
- user.address.street = '10 Downing Street'; // address is 1:1 relation of Address entity
- user.cars.getItems().forEach(car => car.forSale = true); // cars is 1:m collection of Car entities
- const car = new Car('VW');
- user.cars.add(car);
- // now we can flush all changes done to managed entities
- await em.flush();
- ```
- ```sql
- begin;
- update user set title = 'Mr.' where id = 1;
- update user_address set street = '10 Downing Street' where id = 123;
- update car set for_sale = true where id = 1;
- update car set for_sale = true where id = 2;
- update car set for_sale = true where id = 3;
- insert into car (brand, owner) values ('VW', 1);
- commit;
- ```
Only One Instance of Entity
📖 Documentation
You can browse MikroORM v3 docs at https://mikro-orm.io/docs/3.6/installation.
To upgrade to v4, please see the upgrading guide.
✨ Core Features
📦 Example Integrations
TypeScript Examples
JavaScript Examples
Articles
🚀 Quick Start
Since v4, you should install the driver package, but not the db connector itself,
e.g. install @mikro-orm/sqlite, but not sqlite3 as that is already included
in the driver package.
- ```sh
- yarn add @mikro-orm/core @mikro-orm/mongodb # for mongo
- yarn add @mikro-orm/core @mikro-orm/mysql # for mysql/mariadb
- yarn add @mikro-orm/core @mikro-orm/mariadb # for mysql/mariadb
- yarn add @mikro-orm/core @mikro-orm/postgresql # for postgresql
- yarn add @mikro-orm/core @mikro-orm/sqlite # for sqlite
- ```
- ```sh
- npm i -s @mikro-orm/core @mikro-orm/mongodb # for mongo
- npm i -s @mikro-orm/core @mikro-orm/mysql # for mysql/mariadb
- npm i -s @mikro-orm/core @mikro-orm/mariadb # for mysql/mariadb
- npm i -s @mikro-orm/core @mikro-orm/postgresql # for postgresql
- npm i -s @mikro-orm/core @mikro-orm/sqlite # for sqlite
- ```
- ``` json
- "experimentalDecorators": true,
- "emitDecoratorMetadata": true,
- "esModuleInterop": true,
- ```
To access driver specific methods like em.createQueryBuilder() we need to specify
the driver type when calling MikroORM.init(). Alternatively we can cast the
orm.em to EntityManager exported from the driver package:
import { EntityManager } from '@mikro-orm/postgresql';
const em = orm.em as EntityManager;
const qb = em.createQueryBuilder(...);
- ```typescript
- import type { PostgreSqlDriver } from '@mikro-orm/postgresql'; // or any other SQL driver package
- const orm = await MikroORM.init<PostgreSqlDriver>({
- entities: ['./dist/entities'], // path to your JS entities (dist), relative to `baseDir`
- dbName: 'my-db-name',
- type: 'postgresql',
- });
- console.log(orm.em); // access EntityManager via `em` property
- ```
Read more about all the possible configuration options in Advanced Configuration section.
- ```typescript
- const app = express();
- app.use((req, res, next) => {
- RequestContext.create(orm.em, next);
- });
- ```
You should register this middleware as the last one just before request handlers and before
any of your custom middleware that is using the ORM. There might be issues when you register
it before request processing middleware like queryParser or bodyParser, so definitely
register the context after them.
- ```typescript
- @Entity()
- export class MongoBook {
- @PrimaryKey()
- _id: ObjectID;
- @SerializedPrimaryKey()
- id: string;
- @Property()
- title: string;
- @ManyToOne()
- author: Author;
- @ManyToMany()
- tags = new Collection<BookTag>(this);
- constructor(title: string, author: Author) {
- this.title = title;
- this.author = author;
- }
- }
- ```
- ```typescript
- @Entity()
- export class SqlBook {
- @PrimaryKey()
- id: number;
- }
- ```
- ```typescript
- import { v4 } from 'uuid';
- @Entity()
- export class UuidBook {
- @PrimaryKey()
- uuid = v4();
- }
- ```
- ```typescript
- // use constructors in your 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 orm.em.persistAndFlush([book1, book2, book3]);
- ```
- ```typescript
- const authors = orm.em.find(Author, {});
- for (const author of authors) {
- console.log(author); // instance of Author entity
- console.log(author.name); // Jon Snow
- for (const book of author.books) { // iterating books collection
- console.log(book); // instance of Book entity
- console.log(book.title); // My Life on The Wall, part 1/2/3
- }
- }
- ```
- ```typescript
- const booksRepository = orm.em.getRepository(Book);
- // with sorting, limit and offset parameters, populating author references
- const books = await booksRepository.find({ author: '...' }, ['author'], { title: QueryOrder.DESC }, 2, 1);
- // or with options object
- const books = await booksRepository.find({ author: '...' }, {
- populate: ['author'],
- limit: 1,
- offset: 2,
- orderBy: { title: QueryOrder.DESC },
- });
- console.log(books); // Book[]
- ```