From 47cd9a5798a70d57cfb9ac3ec7f2254a77aafdfb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Martin=20Ad=C3=A1mek?= Date: Tue, 15 Sep 2020 16:14:32 +0200 Subject: [PATCH] feat(core): allow using `AsyncLocalStorage` for request context Closes #575 --- docs/docs/async-local-storage.md | 19 +++++++++++++++++++ docs/sidebars.js | 1 + .../version-4.0/async-local-storage.md | 19 +++++++++++++++++++ .../version-4.0-sidebars.json | 4 ++++ packages/core/package.json | 3 +-- packages/core/src/EntityManager.ts | 10 +++++----- packages/core/src/utils/Configuration.ts | 3 +++ packages/core/src/utils/RequestContext.ts | 7 +++++-- 8 files changed, 57 insertions(+), 9 deletions(-) create mode 100644 docs/docs/async-local-storage.md create mode 100644 docs/versioned_docs/version-4.0/async-local-storage.md diff --git a/docs/docs/async-local-storage.md b/docs/docs/async-local-storage.md new file mode 100644 index 000000000000..b6da15b38046 --- /dev/null +++ b/docs/docs/async-local-storage.md @@ -0,0 +1,19 @@ +--- +title: Using `AsyncLocalStorage` +--- + +By default, `domain` api use used in the `RequestContext` helper. Since v4.0.3, +you can use the new `AsyncLocalStorage` too, if you are on up to date node version: + +```typescript +const storage = new AsyncLocalStorage(); + +const orm = await MikroORM.init({ + context: () => storage.getStore(), + // ... +}); + +app.use((req, res, next) => { + storage.run(orm.em.fork(true, true), next); +}); +``` diff --git a/docs/sidebars.js b/docs/sidebars.js index e055f9f8abad..4f8b361cf3b7 100755 --- a/docs/sidebars.js +++ b/docs/sidebars.js @@ -59,6 +59,7 @@ module.exports = { 'entity-constructors', 'multiple-schemas', 'using-bigint-pks', + 'async-local-storage', 'custom-driver', ], 'Example Integrations': [ diff --git a/docs/versioned_docs/version-4.0/async-local-storage.md b/docs/versioned_docs/version-4.0/async-local-storage.md new file mode 100644 index 000000000000..b6da15b38046 --- /dev/null +++ b/docs/versioned_docs/version-4.0/async-local-storage.md @@ -0,0 +1,19 @@ +--- +title: Using `AsyncLocalStorage` +--- + +By default, `domain` api use used in the `RequestContext` helper. Since v4.0.3, +you can use the new `AsyncLocalStorage` too, if you are on up to date node version: + +```typescript +const storage = new AsyncLocalStorage(); + +const orm = await MikroORM.init({ + context: () => storage.getStore(), + // ... +}); + +app.use((req, res, next) => { + storage.run(orm.em.fork(true, true), next); +}); +``` diff --git a/docs/versioned_sidebars/version-4.0-sidebars.json b/docs/versioned_sidebars/version-4.0-sidebars.json index e602b682c6fe..b7655c6d1754 100644 --- a/docs/versioned_sidebars/version-4.0-sidebars.json +++ b/docs/versioned_sidebars/version-4.0-sidebars.json @@ -232,6 +232,10 @@ "type": "doc", "id": "version-4.0/using-bigint-pks" }, + { + "type": "doc", + "id": "version-4.0/async-local-storage" + }, { "type": "doc", "id": "version-4.0/custom-driver" diff --git a/packages/core/package.json b/packages/core/package.json index 7e7a22bc1429..7097ae80f457 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -54,8 +54,7 @@ "fs-extra": "^9.0.1", "globby": "^11.0.1", "reflect-metadata": "^0.1.13", - "strip-json-comments": "^3.1.1", - "uuid": "^8.3.0" + "strip-json-comments": "^3.1.1" }, "peerDependencies": { "@mikro-orm/entity-generator": "^4.0.0", diff --git a/packages/core/src/EntityManager.ts b/packages/core/src/EntityManager.ts index 3decaf274dee..1cebfd90f2c2 100644 --- a/packages/core/src/EntityManager.ts +++ b/packages/core/src/EntityManager.ts @@ -1,7 +1,6 @@ -import { v4 as uuid } from 'uuid'; import { inspect } from 'util'; -import { Configuration, QueryHelper, RequestContext, Utils } from './utils'; +import { Configuration, QueryHelper, Utils } from './utils'; import { AssignOptions, EntityAssigner, EntityFactory, EntityLoader, EntityRepository, EntityValidator, IdentifiedReference, Reference } from './entity'; import { UnitOfWork } from './unit-of-work'; import { CountOptions, DeleteOptions, EntityManagerType, FindOneOptions, FindOneOrFailOptions, FindOptions, IDatabaseDriver, UpdateOptions } from './drivers'; @@ -19,7 +18,8 @@ import { OptimisticLockError, ValidationError } from './errors'; */ export class EntityManager { - readonly id = uuid(); + private static counter = 1; + readonly id = EntityManager.counter++; private readonly validator = new EntityValidator(this.config.get('strict')); private readonly repositoryMap: Dictionary> = {}; private readonly entityLoader: EntityLoader = new EntityLoader(this); @@ -647,7 +647,7 @@ export class EntityManager { * Gets the UnitOfWork used by the EntityManager to coordinate operations. */ getUnitOfWork(): UnitOfWork { - const em = this.useContext ? (RequestContext.getEntityManager() || this) : this; + const em = this.useContext ? (this.config.get('context')() || this) : this; return em.unitOfWork; } @@ -655,7 +655,7 @@ export class EntityManager { * Gets the EntityFactory used by the EntityManager. */ getEntityFactory(): EntityFactory { - const em = this.useContext ? (RequestContext.getEntityManager() || this) : this; + const em = this.useContext ? (this.config.get('context')() || this) : this; return em.entityFactory; } diff --git a/packages/core/src/utils/Configuration.ts b/packages/core/src/utils/Configuration.ts index 6a9e0f0a9573..ce8a17e39824 100644 --- a/packages/core/src/utils/Configuration.ts +++ b/packages/core/src/utils/Configuration.ts @@ -18,6 +18,7 @@ import { EventSubscriber } from '../events'; import { IDatabaseDriver } from '../drivers/IDatabaseDriver'; import { EntityOptions } from '../decorators'; import { NotFoundError } from '../errors'; +import { RequestContext } from './RequestContext'; export class Configuration { @@ -35,6 +36,7 @@ export class Configuration { }, strict: false, validate: false, + context: () => RequestContext.getEntityManager(), // eslint-disable-next-line no-console logger: console.log.bind(console), findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => NotFoundError.findOneFailed(entityName, where), @@ -329,6 +331,7 @@ export interface MikroORMOptions ex replicas?: Partial[]; strict: boolean; validate: boolean; + context: () => EntityManager | undefined; logger: (message: string) => void; findOneOrFailHandler: (entityName: string, where: Dictionary | IPrimaryKey) => Error; debug: boolean | LoggerNamespace[]; diff --git a/packages/core/src/utils/RequestContext.ts b/packages/core/src/utils/RequestContext.ts index 8685f33f6a43..d2017ad867e4 100644 --- a/packages/core/src/utils/RequestContext.ts +++ b/packages/core/src/utils/RequestContext.ts @@ -1,13 +1,16 @@ import domain, { Domain } from 'domain'; -import { v4 as uuid } from 'uuid'; import { EntityManager } from '../EntityManager'; import { Dictionary } from '../typings'; export type ORMDomain = Domain & { __mikro_orm_context?: RequestContext }; +/** + * For node 14 and above it is suggested to use `AsyncLocalStorage` instead, + * @see https://mikro-orm.io/docs/async-local-storage/ + */ export class RequestContext { - readonly id = uuid(); + readonly id = this.em.id; constructor(readonly em: EntityManager) { }