오픈소스/Mikro ORM

Mikro ORM: Installation & Usage

iamjoy 2022. 11. 6. 14:18

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을 사용할 수 있다.

- 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 가 서로 충독하지 않도록 합니다. 

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({
  // ...
});