@mikro-orm/core
Version:
TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.
220 lines (219 loc) • 7.7 kB
JavaScript
import { MetadataDiscovery } from './metadata/MetadataDiscovery.js';
import { MetadataStorage } from './metadata/MetadataStorage.js';
import { Configuration } from './utils/Configuration.js';
import { loadEnvironmentVars } from './utils/env-vars.js';
import { Utils } from './utils/Utils.js';
import { colors } from './logging/colors.js';
async function tryRegisterExtension(name, pkg, extensions) {
try {
const url = import.meta.resolve(pkg);
const mod = await import(url);
if (mod[name]) {
extensions.push(mod[name]);
}
}
catch {
// not installed
}
}
/** @internal */
export async function loadOptionalDependencies(options) {
await import('@mikro-orm/core/fs-utils').then(m => m.fs.init()).catch(() => null);
const extensions = options.extensions ?? [];
const exists = (name) => extensions.some(ext => ext.name === name);
if (!exists('SeedManager')) {
await tryRegisterExtension('SeedManager', '@mikro-orm/seeder', extensions);
}
if (!exists('Migrator')) {
await tryRegisterExtension('Migrator', '@mikro-orm/migrations', extensions);
}
/* v8 ignore if */
if (!exists('Migrator')) {
await tryRegisterExtension('Migrator', '@mikro-orm/migrations-mongodb', extensions);
}
if (!exists('EntityGenerator')) {
await tryRegisterExtension('EntityGenerator', '@mikro-orm/entity-generator', extensions);
}
options.extensions = extensions;
const metadataCacheEnabled = options.metadataCache?.enabled || options.metadataProvider?.useCache?.();
if (metadataCacheEnabled) {
options.metadataCache ??= {};
options.metadataCache.adapter ??= await import('@mikro-orm/core/fs-utils').then(m => m.FileCacheAdapter);
}
}
/**
* The main class used to configure and bootstrap the ORM.
*
* @example
* ```ts
* // import from driver package
* import { MikroORM, defineEntity, p } from '@mikro-orm/sqlite';
*
* const User = defineEntity({
* name: 'User',
* properties: {
* id: p.integer().primary(),
* name: p.string(),
* },
* });
*
* const orm = new MikroORM({
* entities: [User],
* dbName: 'my.db',
* });
* await orm.schema.update();
*
* const em = orm.em.fork();
* const u1 = em.create(User, { name: 'John' });
* const u2 = em.create(User, { name: 'Ben' });
* await em.flush();
* ```
*/
export class MikroORM {
/** The global EntityManager instance. If you are using `RequestContext` helper, it will automatically pick the request specific context under the hood */
em;
/** The database driver instance used by this ORM. */
driver;
/** The ORM configuration instance. */
config;
#metadata;
#logger;
#discovery;
/**
* Initialize the ORM, load entity metadata, create EntityManager and connect to the database.
* If you omit the `options` parameter, your CLI config will be used.
*/
static async init(options) {
/* v8 ignore next */
if (!options) {
throw new Error(`options parameter is required`);
}
options = { ...options };
options.discovery ??= {};
options.discovery.skipSyncDiscovery ??= true;
await loadOptionalDependencies(options);
const orm = new this(options);
const preferTs = orm.config.get('preferTs', Utils.detectTypeScriptSupport());
orm.#metadata = await orm.#discovery.discover(preferTs);
orm.createEntityManager();
return orm;
}
/**
* Synchronous variant of the `init` method with some limitations:
* - folder-based discovery not supported
* - ORM extensions are not autoloaded
* - when metadata cache is enabled, `FileCacheAdapter` needs to be explicitly set in the config
*/
constructor(options) {
const env = loadEnvironmentVars();
options = options.preferEnvVars ? Utils.merge(options, env) : Utils.merge(env, options);
this.config = new Configuration(options);
const discovery = this.config.get('discovery');
this.driver = this.config.getDriver();
this.#logger = this.config.getLogger();
this.#logger.log('info', `MikroORM version: ${colors.green(Utils.getORMVersion())}`);
this.#discovery = new MetadataDiscovery(new MetadataStorage(), this.driver.getPlatform(), this.config);
this.driver.getPlatform().init(this);
for (const extension of this.config.get('extensions')) {
extension.register(this);
}
if (!discovery.skipSyncDiscovery) {
this.#metadata = this.#discovery.discoverSync();
this.createEntityManager();
}
}
/**
* Connects to the database.
*/
async connect() {
await this.driver.connect();
return this.driver;
}
/**
* Reconnects, possibly to a different database.
*/
async reconnect(options = {}) {
/* v8 ignore next */
for (const key of Utils.keys(options)) {
this.config.set(key, options[key]);
}
await this.driver.reconnect();
}
/**
* Checks whether the database connection is active.
*/
async isConnected() {
return this.driver.getConnection().isConnected();
}
/**
* Checks whether the database connection is active, returns the reason if not.
*/
async checkConnection() {
return this.driver.getConnection().checkConnection();
}
/**
* Closes the database connection.
*/
async close(force = false) {
await this.driver.close(force);
await this.config.getMetadataCacheAdapter()?.close?.();
await this.config.getResultCacheAdapter()?.close?.();
}
/**
* Gets the `MetadataStorage` (without parameters) or `EntityMetadata` instance when provided with the `entityName` parameter.
*/
getMetadata(entityName) {
if (entityName) {
return this.#metadata.get(entityName);
}
return this.#metadata;
}
createEntityManager() {
this.driver.setMetadata(this.#metadata);
this.em = this.driver.createEntityManager();
this.em.global = true;
this.#metadata.decorate(this.em);
}
/**
* Allows dynamically discovering new entity by reference, handy for testing schema diffing.
*/
discoverEntity(entities, reset) {
for (const className of Utils.asArray(reset)) {
this.#metadata.reset(className);
this.#discovery.reset(className);
}
const tmp = this.#discovery.discoverReferences(Utils.asArray(entities));
const metadata = this.#discovery.processDiscoveredEntities(tmp);
for (const meta of metadata) {
this.#metadata.set(meta.class, meta);
meta.root = this.#metadata.get(meta.root.class);
}
this.#metadata.decorate(this.em);
}
/**
* Gets the SchemaGenerator.
*/
get schema() {
return this.config.getExtension('@mikro-orm/schema-generator');
}
/**
* Gets the SeedManager
*/
get seeder() {
return this.driver.getPlatform().getExtension('SeedManager', '@mikro-orm/seeder', '@mikro-orm/seeder', this.em);
}
/**
* Gets the Migrator.
*/
get migrator() {
return this.driver.getPlatform().getExtension('Migrator', '@mikro-orm/migrator', '@mikro-orm/migrations', this.em);
}
/**
* Gets the EntityGenerator.
*/
get entityGenerator() {
return this.driver
.getPlatform()
.getExtension('EntityGenerator', '@mikro-orm/entity-generator', '@mikro-orm/entity-generator', this.em);
}
}