Skip to content

What's New in MikroORM 7? Complete Feature Guide

After 18 months of development, MikroORM 7 “Unchained” has landed. I’ve been using MikroORM on several projects, and this release addresses many pain points I’ve encountered - especially the lack of polymorphic relations. Let me walk you through the changes that actually matter for day-to-day development.

Why “Unchained”?

The name isn’t marketing fluff. The core package now has zero runtime dependencies. Previously, pulling in @mikro-orm/core meant dragging Knex and its dependency tree along. Now? Nothing. This isn’t just about bundle size - it opens the door for Deno, Cloudflare Workers, and other edge runtimes.

The Feature I’ve Been Waiting For: Polymorphic Relations

This was the most requested feature, and for good reason. If you’ve ever built a commenting system that needs to work with multiple entity types, you know the pain. Here’s how it works now:

comment.entity.ts
@Entity()
abstract class Content {
@PrimaryKey()
id!: number
@Property()
title!: string
}
@Entity()
class Article extends Content {
@Property()
text!: string
}
@Entity()
class Video extends Content {
@Property()
url!: string
}
@Entity()
class Comment {
@PrimaryKey()
id!: number
// This is the magic - one relation, multiple target types
@ManyToOne(() => Content, { polymorphic: true })
content!: Content // Can be Article or Video
@Property()
text!: string
}

Query works exactly as you’d expect:

query.ts
const comments = await em.find(Comment, {}, {
populate: ['content']
})
// Access the concrete type
for (const comment of comments) {
if (comment.content instanceof Article) {
console.log('Article:', comment.content.title)
} else if (comment.content instanceof Video) {
console.log('Video:', comment.content.url)
}
}

Why does this matter? Before v7, you’d need separate comment tables for each content type or a complex join table setup. Now it’s one clean relation.

Streaming for Large Datasets

If you’ve ever run out of memory processing a large table, this one’s for you:

streaming.ts
// Process millions of records without loading them all
for await (const user of em.stream(User, {})) {
await sendNewsletter(user)
}
// Works with QueryBuilder too
const stream = em.createQueryBuilder(User, 'u')
.where({ active: true })
.stream()
for await (const user of stream) {
await processUser(user)
}

The streaming implementation uses database cursors under the hood, so you’re not buffering everything in memory. I used this for a data migration job that previously crashed my Node process - it now runs smoothly.

Kysely Replaces Knex

The query building layer has moved from Knex to Kysely. For most developers, this is transparent - the QueryBuilder API hasn’t changed. But there are benefits:

  • Better TypeScript inference (Kysely was built for type safety)
  • More active development (Knex has slowed down)
  • Cleaner internal architecture

If you’re using raw SQL or the createQueryBuilder() method, you might notice better autocomplete and type checking. The migration was handled internally, so your existing queries should work without changes.

Collection Size Queries Without Loading

A small but useful addition - you can now query by collection size:

size-query.ts
// Find users with more than 5 posts
const activeUsers = await em.find(User, {
posts: { $size: { $gt: 5 } }
})
// Find categories with exactly 10 items
const specificCategories = await em.find(Category, {
items: { $size: 10 }
})

Previously, this required custom SQL or loading collections first. Now it’s a one-liner.

Common Table Expressions (CTEs)

For complex queries, CTEs are now supported:

cte.ts
// Recursive CTE for hierarchical data
const result = await em.createQueryBuilder(Category, 'c')
.withCTE('category_tree', qb => qb
.select(['id', 'name', 'parent_id'])
.from('categories')
.where({ parent_id: null })
)
.from('category_tree')
.getResult()

This opens up patterns like traversing hierarchical data, complex aggregations, and multi-step transformations that were painful to write before.

View Entities and Materialized Views

PostgreSQL views are now first-class citizens:

view-entity.ts
@Entity({ view: true })
class UserPostCount {
@PrimaryKey()
userId!: number
@Property()
userName!: string
@Property()
postCount!: number
}
// Materialized views for caching heavy queries
@Entity({ view: true, materialized: true })
class CachedStatistics {
@PrimaryKey()
id!: number
@Property()
value!: number
}
// Refresh when needed
await em.refresh(CachedStatistics)

This is particularly useful for read-heavy dashboards where you don’t need real-time data.

Performance: 40% Fewer Type Instantiations

The MikroORM team optimized TypeScript inference, resulting in 40% fewer type instantiations. In practice, this means:

  • Faster tsc compilation times
  • Better IDE performance
  • Less memory usage during builds

On a project with 200+ entities, I saw compilation time drop from 45 seconds to 28 seconds. Your mileage will vary, but it’s a noticeable improvement.

Oracle Database Support

MikroORM now supports 8 databases: PostgreSQL, MySQL, MariaDB, SQLite, MongoDB, LibSQL, MS SQL Server, and Oracle. If you’re in an enterprise environment stuck with Oracle, this is your green light to use MikroORM.

oracle-config.ts
import { OracleDriver } from '@mikro-orm/oracle'
export default defineConfig({
driver: OracleDriver,
host: 'localhost',
port: 1521,
dbName: 'ORCL',
})

Migration Gotchas

Before upgrading from v6, be aware of these breaking changes:

  1. forceUtcTimezone defaults to true - If your app relies on local timezone handling, explicitly set this to false or audit your datetime handling
  2. Kysely is now a required peer dependency - Run npm install kysely
  3. mikro-orm-esm script is gone - Use the unified CLI

Check the migration guide for the full list of changes.

When Should You Upgrade?

  • New projects: Absolutely. Start with v7.
  • Existing projects: Wait for v7.1 if you rely heavily on datetime handling or have complex migrations. The breaking changes are manageable but require testing.
  • Edge/serverless projects: Now. The zero-dependency core and pre-compiled entity support make MikroORM viable for Cloudflare Workers and similar environments.

Final Words + More Resources

My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me

Here are also the most important links from this article along with some further resources that will help you in this scope:

Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!

Comments