Mikro ORM: Installation & Usage
Installation
(https://mikro-orm.io/docs/installation)
- v4 부터 db connector 자체가 아니라 driver를 설치하면 그 안에 db connector가 들어있다.
- decorator(https://www.typescriptlang.org/docs/handbook/decorators.html)를 사용하기 위해서 tsconfig.json에 아래와 같은 설정을 해준다.
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true,
- mikro orm을 시작시키기 위해 , MikroORM.init 호출한다.
- em.createQueryBuilder()과 같이 특정 driver에 의존적인 매서드에 접근하기 위해서는 MikroORM.init<D>() 이런 방식으로 mikro orm을 호출해야한다.
- entities 는 build 된 js 파일을 보고 entitiesTs는 TS를 본다.
import type { PostgreSqlDriver } from '@mikro-orm/postgresql'; // or any other driver package
const orm = await MikroORM.init<PostgreSqlDriver>({
entities: ['./dist/entities'], // path to our JS entities (dist), relative to `baseDir`
entitiesTs: ['./src/entities'], // path to our TS entities (src), relative to `baseDir`
dbName: 'my-db-name',
type: 'postgresql',
});
console.log(orm.em); // access EntityManager via `em` property
- 다른 방법으로는 orm.em을 특정 드라이버에서 가져오는 EntityManager로 타입캐스팅 해서 사용하는 방법이 있다.
import { EntityManager } from '@mikro-orm/postgresql';
const em = orm.em as EntityManager;
const qb = em.createQueryBuilder(...);
+ Mikro ORM Configuration(https://mikro-orm.io/docs/configuration) 에서 조금 더 구체적인 설정들을 할 수 있다.
Possible issues with circular dependencies
(https://mikro-orm.io/docs/installation#possible-issues-with-circular-dependencies)
- entity를 저장하는 곳을 지정할 수 도 있다. 내부적으로 globby(https://github.com/sindresorhus/globby)를 사용하고 있으므로 globby pattern을 사용할 수 있다.
- 또는 configuration 에서 env variable을 설정할 수 있다.(https://mikro-orm.io/docs/configuration#using-environment-variables)
- 개별 db driver에서 설정할 수도 있는데 이런 경우 driver 설정이 mikro orm 설정을 override 할 수 있다.
- Circular Dependency 이슈 문제
- folder base로 entity를 찾게 설정해두면 양방향 연관관계 같은 곳에서 아래 같은 모양의 circular dependency 문제가 생길 수 있다.
TypeError: Cannot read property 'name' of undefined
at Function.className (/path/to/project/node_modules/mikro-orm/dist/utils/Utils.js:253:28)
at TsMorphMetadataProvider.extractType (/path/to/project/node_modules/mikro-orm/dist/metadata/TsMorphMetadataProvider.js:37:34)
at TsMorphMetadataProvider.initProperties (/path/to/project/node_modules/mikro-orm/dist/metadata/TsMorphMetadataProvider.js:25:31)
at TsMorphMetadataProvider.loadEntityMetadata (/path/to/project/node_modules/mikro-orm/dist/metadata/TsMorphMetadataProvider.js:16:9)
at MetadataDiscovery.discoverEntity (/path/to/project/node_modules/mikro-orm/dist/metadata/MetadataDiscovery.js:109:9)
at MetadataDiscovery.discoverDirectory (/path/to/project/node_modules/mikro-orm/dist/metadata/MetadataDiscovery.js:80:13)
at Function.runSerial (/path/to/project/node_modules/mikro-orm/dist/utils/Utils.js:303:22)
at MetadataDiscovery.findEntities (/path/to/project/node_modules/mikro-orm/dist/metadata/MetadataDiscovery.js:56:13)
at MetadataDiscovery.discover (/path/to/project/node_modules/mikro-orm/dist/metadata/MetadataDiscovery.js:30:9)
at Function.init (/path/to/project/node_modules/mikro-orm/dist/MikroORM.js:45:24)
at Function.handleSchemaCommand (/path/to/project/node_modules/mikro-orm/dist/cli/SchemaCommandFactory.js:51:21)
- 개체 배열에서 개체 참조를 사용하여 검색 순서를 제어합니다. 여기에서 제공하는 실제 순서나 가져오기 명령문의 순서로 플레이해야 할 수도 있습니다.
- 참조 대신 문자열을 사용하세요(예: @OneToMany('Book', 'author')). 여기서 단점은 데코레이터의 유형 검사 기능을 잃게 된다는 것입니다.
TS에서 Entity를 찾는 방법
(https://mikro-orm.io/docs/installation#entity-discovery-in-typescript)
- Mikro ORM은 ReflectMetadataProvider 에서 metadata를 기본값으로 가지고 있습니다. ts-morph 를 기반으로 entity를 찾기 위해서는 @mikro-orm/reflection 를 install 해야합니다.(ts-morph: https://github.com/dsherret/ts-morph)
[참고] Mikro ORM의 Meatadata Providers (https://mikro-orm.io/docs/metadata-providers)
- ts-morph 검색이 프로덕션에서 작동하려면 .d.ts 선언 파일을 배포해야 합니다. tsconfig.json에서 compilerOptions.declaration을 활성화해야 합니다.
// MetadataProvider 가 가지고 있는 정보
export declare abstract class MetadataProvider {
protected readonly config: IConfiguration; // config 정보를 넣어주면
constructor(config: IConfiguration);
abstract loadEntityMetadata(meta: EntityMetadata, name: string): Promise<void>;
/**
* Re-hydrates missing attributes like `customType` (functions/instances are lost when caching to JSON)
*/
loadFromCache(meta: EntityMetadata, cache: EntityMetadata): void;
useCache(): boolean;
protected initProperties(meta: EntityMetadata, fallback: (prop: EntityProperty) => void | Promise<void>): Promise<void>; // entity properties를 가지고 온다.
}
- EntitySchema 를 작성하는 것도 entity를 정의하는 한 가지 방법 중 하나이면서, JavaScriptMetadataProvider를 사용하는 것보다 더 정확하게 엔티티를 정의할 수 있습니다. EntitySchema(https://mikro-orm.io/docs/entity-schema)
Request Context:
- 각 request마다 Entity Manager를 포크해서 identity maps 가 서로 충독하지 않도록 합니다.
- RequestContext helper(https://mikro-orm.io/docs/identity-map#request-context)
const app = express();
app.use((req, res, next) => {
RequestContext.create(orm.em, next);
});
Setting up the Commandline Tool
이번 작업에서는 CLI를 사용하지 않아서 config 관련 내용 중 관심있는 부분만 확인.
(https://mikro-orm.io/docs/installation#possible-issues-with-circular-dependencies)
MikroORM 은 configPaths에서 찾는 순서대로 해서 가장 처음으로 찾는 config file을 사용한다. 예를 들어 아래와 같이 설정되어 있는데 ts-node가 없으면 ts 파일을 읽을 수 없어서 에러가 발생한다.
{
"name": "our-app",
"dependencies": { ... },
"mikro-orm": {
"useTsNode": true,
"configPaths": [
"./src/mikro-orm.config.ts",
"./dist/mikro-orm.config.js"
]
}
}
이렇게 순서대로 읽으면서 에러가 발생하는 것을 막기 위해서 Option이나 defindeConfig를 사용해서 어떤 config를 사용할 지 명시해주는 방법이 있다.
// 옵션 설정하기
import { Options } from '@mikro-orm/core';
const config: Options = {
// ...
};
export default config;
// config 파일 정의하기
import { defineConfig } from '@mikro-orm/core';
export default defineConfig({
// ...
});