I'm currently running an all-typescript project with Bun + Elysia. I've migrated my app from a previous NestJs project, which was running MikroORM just fine. Now that I've had my app running via bun
, the migrator doesn't work:
bunx --bun mikro-orm
with the following error:
354 | /* istanbul ignore next */
355 | if ('type' in this.options) {
356 | throw new Error('The `type` option has been removed in v6, please fill in the `driver` option instead or use `defineConfig` helper (to define your ORM config) or `MikroORM` class (to call the `init` method) exported from the driver package (e.g. `import { defineConfig } from \'@mikro-orm/mysql\'; export default defineConfig({ ... })`).');
357 | }
358 | if (!this.options.driver) {
359 | throw new Error('No driver specified, please fill in the `driver` option or use `defineConfig` helper (to define your ORM config) or `MikroORM` class (to call the `init` method) exported from the driver package (e.g. `import { defineConfig } from \'@mikro-orm/mysql\'; export defineConfig({ ... })`).');
^
error: No driver specified, please fill in the `driver` option or use `defineConfig` helper (to define your ORM config) or `MikroORM` class (to call the `init` method) exported from the driver package (e.g. `import { defineConfig } from '@mikro-orm/mysql'; export defineConfig({ ... })`).
at validateOptions (C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\utils\Configuration.js:359:19)
at new Configuration (C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\utils\Configuration.js:140:13)
at C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\utils\ConfigurationLoader.js:38:24
error: script "orm" exited with code 1
But I've specified everything in src/orm.ts
file properly like this:
// orm.ts
import { Migrator } from '@mikro-orm/migrations'
import { SeedManager } from '@mikro-orm/seeder'
import { MikroORM, ReflectMetadataProvider, SqliteDriver, defineConfig } from '@mikro-orm/sqlite'
import { User } from '#user/user.entity'
import { Role } from '#role/role.entity'
import { Ability } from '#role/ability.entity'
import { Link } from '#link/link.entity'
export const config = defineConfig({
debug: true,
dbName: 'smm.db',
driver: SqliteDriver,
entities: [
Role,
Ability,
User,
Link
],
extensions: [Migrator, SeedManager],
migrations: {
path: './src/data/migrations',
transactional: true,
emit: 'ts',
snapshot: true
},
seeder: {
path: './src/**/',
glob: '!(*.d)(*.seeder).{js,ts}',
emit: 'ts',
defaultSeeder: 'DatabaseSeeder'
},
metadataProvider: ReflectMetadataProvider
})
export const orm = MikroORM.initSync(config)
export default orm
I'm running the orm with this command:
"orm": "bunx --bun mikro-orm"
This is the package.json
file. Note that I do not wanna compile and produce js
files:
"name": "smm_server",
"version": "0.0.1",
"module": "src/index.ts",
"type": "module",
"scripts": {
"dev": "bun run --watch src/index.ts",
"inspect": "bun --inspect src/index.ts",
"format": "biome format .",
"lint": "biome lint .",
"check": "biome check --apply .",
"test": "bun test",
"typecheck": "tsc --noEmit --project tsconfig.json",
"orm": "bunx --bun mikro-orm"
},
"dependencies": {
"@elysiajs/cors": "1.0.2",
"@elysiajs/eden": "1.0.14",
"@elysiajs/jwt": "^1.0.2",
"@elysiajs/static": "1.0.3",
"@elysiajs/swagger": "1.0.5",
"@faker-js/faker": "^8.4.1",
"@mikro-orm/core": "^6.2.9",
"@mikro-orm/migrations": "^6.2.9",
"@mikro-orm/mysql": "^6.2.9",
"@mikro-orm/reflection": "^6.2.9",
"@mikro-orm/seeder": "^6.2.9",
"@mikro-orm/sqlite": "^6.2.9",
"@types/uuid": "^9.0.8",
"elysia": "1.0.22",
"elysia-autoroutes": "0.5.0",
"pino": "9.1.0",
"reflect-metadata": "^0.2.2",
"sqlite3": "^5.1.7",
"uuid": "^9.0.1"
},
"devDependencies": {
"@biomejs/biome": "1.7.3",
"@mikro-orm/cli": "^6.2.9",
"bun-types": "1.1.10",
"tslib": "2.6.2",
"typescript": "5.4.5"
},
"mikro-orm": {
"alwaysAllowTs": true,
"configPaths": [
"./src/data/orm.ts"
]
},
"trustedDependencies": [
"sqlite3"
]
}
Also the tsconfig.json
file:
{
"compilerOptions": {
// Enable latest features
"lib": ["ES2022"],
"target": "ES2022",
"module": "Node16",
"moduleDetection": "force",
"jsx": "react-jsx",
"allowJs": true,
// Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"verbatimModuleSyntax": true,
"noEmit": true,
// Best practices
"strict": true,
"skipLibCheck": true,
"noFallthroughCasesInSwitch": true,
"types": ["bun-types"],
// Some stricter flags
"noUnusedLocals": true,
"noUnusedParameters": true,
"noPropertyAccessFromIndexSignature": true,
"forceConsistentCasingInFileNames": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"baseUrl": ".",
"paths": {
"#auth/*": ["./src/auth/*"],
"#data/*": ["./src/data/*"],
"#health/*": ["./src/health/*"],
"#link/*": ["./src/link/*"],
"#page/*": ["./src/page/*"],
"#role/*": ["./src/role/*"],
"#user/*": ["./src/user/*"],
"#logger": ["./src/logger.ts"]
}
},
"include": ["**/*.ts", "**/*.tsx"],
"exclude": ["node_modules"],
}
I'm also adding two of these entities here:
import { Link } from '#link/link.entity'
import { Role, RoleStandard } from '#role/role.entity'
import { BeforeCreate, BeforeUpdate, Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { v4 } from 'uuid'
const SALT_ROUNDS = 10
@Entity({
tableName: 'users'
})
export class User {
@PrimaryKey({
name: 'uuid',
type: 'text',
autoincrement: false,
nullable: false,
unique: true,
comment: 'User\'s ID',
})
public uuid?: string = v4()
@Property({
name: 'username',
type: 'varchar',
length: 32,
unique: true,
nullable: false,
comment: 'User\'s username'
})
public username?: string
@Property({
name: 'email',
type: 'varchar',
length: 64,
unique: true,
nullable: false,
comment: 'User\'s email'
})
public email?: string
@Property({
name: 'display_name',
type: 'varchar',
length: 32,
nullable: true,
comment: 'User\'s display name'
})
public display_name?: string | null
@Property({
name: 'password',
type: 'varchar',
length: 64,
nullable: false,
comment: 'User\'s password'
})
public password?: string
@Property({
name: 'coins',
type: 'integer',
unsigned: true,
nullable: false,
default: 0,
comment: 'User\'s coins'
})
public coins?: number
@Property({
name: 'created_at',
type: 'datetime',
nullable: false,
comment: 'User\'s creation date and time'
})
public createdAt?: Date = new Date()
/**
* Updated at
*/
@Property({
name: 'updated_at',
type: 'datetime',
nullable: false,
onUpdate: () => new Date(),
comment: 'User\'s update date and time'
})
public updatedAt?: Date = new Date()
@ManyToOne({
entity: () => Role,
nullable: false,
deleteRule: 'set null',
})
public role?: Role
@OneToMany({
entity: () => Link,
mappedBy: link => link.user,
orphanRemoval: false
})
public links? = new Collection<Link>(this)
@BeforeCreate()
@BeforeUpdate()
async hashPassword() {
if (this.password) {
this.password = await Bun.password.hash(this.password, {
algorithm: 'bcrypt',
cost: SALT_ROUNDS
})
}
}
@BeforeCreate()
async setDefaultRole() {
if (!this.role) {
this.role = RoleStandard
}
}
}
And role.entity.ts
:
import { User } from '#user/user.entity'
import { Collection, Entity, OneToMany, PrimaryKey, Property } from '@mikro-orm/core'
import { Ability } from '#role/ability.entity'
@Entity({
tableName: 'roles'
})
export class Role {
constructor(id?: number, name?: string) {
this.id = id
this.name = name
}
@PrimaryKey({
name: 'id',
type: 'integer',
autoincrement: false,
comment: 'Role\'s ID',
})
public id?: number
@Property({
name: 'name',
type: 'varchar',
length: 16,
unique: true,
nullable: false,
comment: 'Role\'s name'
})
public name?: string
@OneToMany({
entity: () => Ability,
mappedBy: permission => permission.role,
orphanRemoval: false
})
public abilities? = new Collection<Ability>(this)
@OneToMany({
entity: () => User,
mappedBy: user => user.role,
orphanRemoval: false
})
public users? = new Collection<User>(this)
}
export const RoleAdmin: Role = { id: 1, name: 'Admin' }
export const RolePremium: Role = { id: 2, name: 'Premium' }
export const RoleStandard: Role = { id: 3, name: 'Standard' }
export const RoleBanned: Role = { id: 4, name: 'Banned' }
Seriously I'm out of clues. When I use the orm command without MikroORM.init()
(so just defineConfig()
) it complains about all entities being abstract. So in the file orm.ts
instead of exporting orm
if I export config
, it will output:
172 | static noEntityDiscovered() {
173 | return new MetadataError('No entities were discovered');
174 | }
175 | static onlyAbstractEntitiesDiscovered() {
176 | return new MetadataError('Only abstract entities were discovered, maybe you forgot to use @Entity() decorator?');
177 | }
^
MetadataError: Only abstract entities were discovered, maybe you forgot to use @Entity() decorator?
at onlyAbstractEntitiesDiscovered (C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\errors.js:177:11)
at validateDiscovered (C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\metadata\MetadataValidator.js:117:66)
at findEntities (C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\metadata\MetadataDiscovery.js:193:17)
at C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\metadata\MetadataDiscovery.js:63:14
at discover (C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\metadata\MetadataDiscovery.js:59:25)
at C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\MikroORM.js:172:33
at discoverEntities (C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\MikroORM.js:172:33)
at C:\Users\X\Projects\smm_est\server\node_modules\@mikro-orm\cli\node_modules\@mikro-orm\core\MikroORM.js:80:13
at processTicksAndRejections (native:1:1)
error: script "orm" exited with code 1
This thing has been getting on my nerve lately. I thought about moving to Drizzle, but with Mikro I have the ability to switch to MySql later easily.
I think your problem is not using default export for the ORM config. Don't mix your config with other stuff, especially with code with side effects like yours, that is a bad idea. Put the config separately, preferably to mikro-orm.config.ts
file in the root (and don't forget to use default export), then it should just work.