この投稿は、TypeScript用ORMであるMikro ORMの初歩的なチュートリアルです。データベースはPostgreSQLを用います。
このチュートリアルで学べること
- Mikro ORMのインストール方法
- Mikro ORMの設定方法
- Mikro ORMでのエンティティの
INSERT
とSELECT
のやり方
PostgreSQLを起動する
Mikro ORMは様々なデータベースに対応していますが、今回はPostgreSQLを使いたいので、PostgreSQLサーバーを起動しておきます。
PostgreSQLサーバーを起動するには、Dockerを使うのが手軽なので、それを使います。
docker-compose.ymlを用意します:
version: '3.7'
services:
db:
image: postgres:11.3
# Make postgres logs. More information about logging, see official documentation: https://www.postgresql.org/docs/11/runtime-config-logging.html
command: postgres -c log_destination=stderr -c log_statement=all -c log_connections=on -c log_disconnections=on
ports:
- 5432:5432
environment:
POSTGRES_USER: test
POSTGRES_PASSWORD: test
POSTGRES_INITDB_ARGS: "--encoding=UTF-8"
restart: always
user: root
logging:
options:
max-size: "10k"
max-file: "5"
この設定では、Dockerホストからアクセスできるようにポート5432を開放します。PostgreSQLのユーザはtestにし、パスワードはtestにしてあります。command
にわちゃわちゃ書いてあるのはクエリーログを見れるようにするためのものです。
Docker ComposeでPostgreSQLサーバーを起動します:
docker-compose up
起動したらDockerホスト側からの接続テストを行います。接続テストにはpsql
コマンドを使います。このコマンドがMacOSに入っていない場合はbrew install postgresql
してください。
PGPASSWORD=test psql \
--host 127.0.0.1 \
--port 5432 \
--username test \
--dbname test
このコマンドでプロンプトが立ち上がれば、接続OKです。
必要なパッケージをインストールする
必要なパッケージをインストールします。まず、TypeScriptは必須なので入れます。TypeScriptをコンパイルなしに実行したいので、ts-node
も入れます。
yarn add -D typescript ts-node
次にMikro ORMのコアと、今回はデータベースはPostgreSQLを使うので、そのコネクターをインストールします。TypeScriptで定義したエンティティの型情報はしっかり認識させたいので、@mikro-orm/reflection
も入れておきます:
yarn add @mikro-orm/core @mikro-orm/postgresql @mikro-orm/reflection
Mikro ORMのCLIツールも入れます:
yarn add -D @mikro-orm/cli
Mikro ORMのCLIツールがインストールできているか確認します:
$ npx mikro-orm --version
4.4.4
TypeScriptのコンパイルオプションを設定する
Mikro ORMはデコレーターを使うので、TypeScriptのコンパイルオプションでそれを有効にします。
まず、TypeScriptの設定ファイルを生成します:
npx tsc -init
次に、デコレーター周りのオプションを有効化します:
{
"compilerOptions": {
/* ... */
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"esModuleInterop": true
}
}
Mikro ORMの設定ファイルを作る
データベースの接続情報などを書いておきます。
import { Options } from "@mikro-orm/core";
import { PostgreSqlDriver } from "@mikro-orm/postgresql";
import { TsMorphMetadataProvider } from "@mikro-orm/reflection";
const options: Options<PostgreSqlDriver> = {
driver: PostgreSqlDriver,
metadataProvider: TsMorphMetadataProvider,
entities: ["./entities/*.js"],
entitiesTs: ["./entities/*.ts"],
clientUrl: "postgresql://test:test@127.0.0.1:5432/test",
};
export default options;
このファイルはMikro ORM CLIでデータベースを扱うときに使います。アプリ側からも同じ設定を使っていい場合は、このファイルをimport
して流用できます。
metadataProvider
はエンティティのメタデータをどの形式で書くかを決めるものです。Mikro ORMではいくつかの形式をサポートしていますが、TypeScriptの場合もっとも記述量が少なくて済むTsMorphMetadataProvider
がオススメです。
entitiesTs
では、どのファイルにエンティティがあるかを指定します。これをヒントにMikro ORMはエンティティを探し出します。
clientUrl
はデータベースの接続先URLです。設定ファイルでは、これをhost
, port
, user
, password
, dbName
に分解して書くこともできます。
Mikro ORM CLIはデフォルトだとTypeScriptで書いた設定ファイルmikro-orm.config.tsが認識できないので、package.jsonに下記の設定を加えておきます:
{
/*...*/
"mikro-orm": {
"useTsNode": true,
"configPaths": [
"./src/mikro-orm.config.ts",
"./dist/mikro-orm.config.js"
]
}
}
エンティティを作る
Mikro ORMを動かすには、最低限1つ以上のエンティティが必要なので、試しにBookエンティティを作ります。エンティティはentities
ディレクトリを作り、そこに置くようにします。
import { Entity, PrimaryKey, Property } from "@mikro-orm/core";
@Entity()
export class Book {
@PrimaryKey()
id!: number;
@Property()
title!: string;
}
データベーススキーマを更新する
Bookのエンティティが準備できたら、このエンティティを保存するテーブルをデータベースに作るためにデータベーススキーマの更新をかけます。スキーマの更新にはMikro ORM CLIを使います。
いきなりスキーマ更新をかけてもいいですが、その前にどのようなSQLが実行されるのか確認しておきます:
npx mikro-orm schema:update --dump
このコマンドを実行すると、どのようにスキーマが更新されるかが分かります:
set names 'utf8';
set session_replication_role = 'replica';
create table "book" ("id" serial primary key, "title" varchar(255) not null);
set session_replication_role = 'origin';
更新内容が良さそうなので、このSQLを実行させます。
pnpx mikro-orm schema:update --run
psqlでデータベースのテーブルを確認してみると、テーブルが作られていることが分かります:
PGPASSWORD=test psql \
--host 127.0.0.1 \
--port 5432 \
--username test \
--dbname test \
-c '\d'
出力結果:
List of relations
Schema | Name | Type | Owner
--------+-------------+----------+-------
public | book | table | test
public | book_id_seq | sequence | test
(2 rows)
エンティティの保存とロード
データベースの準備ができたので、エンティティの保存(INSERT
)と、保存したエンティティのロード(SELECT
)をやってみましょう。
main.tsというファイルを作り、次のようなコードを書きます:
import { MikroORM } from "@mikro-orm/core";
import { Book } from "./entities/book";
import config from "./mikro-orm.config";
async function main() {
// ORMの初期化
const orm = await MikroORM.init(config);
// Bookレポジトリを取得する
const bookRepository = orm.em.getRepository(Book);
// Bookエンティティを作る
const newBook = new Book();
newBook.title = "サバイバルTypeScript";
// Bookエンティティを保存する
await bookRepository.persistAndFlush(newBook);
console.log({ newBook });
//=> { newBook: Book { title: 'サバイバルTypeScript', id: 1 } }
// 保存したBookエンティティを取り出す
const books = await bookRepository.findAll();
console.log({ books });
//=> { books: [ Book { title: 'サバイバルTypeScript', id: 1 } ] }
}
main()
.then(() => process.exit())
.catch(console.error);
保存のメソッドがpersistAndFlush
となっていますが、Mikro ORMでは、保存予定のエンティティをマークする処理persist
と実際にエンティティを保存する処理flush
に分かれているためこのようなネーミングになっています。persist
とflush
は別々に呼び出すことができます。persistAndFlush
はそれら2つを一度に呼び出すためのショートカットです。このへんのメカニズムの詳細は公式ドキュメントを御覧ください: Working with Entity Manager | MikroORM
このスクリプトはTypeScriptで書いているので、ts-nodeで実行します:
pnpx ts-node main.ts
実行するとbookテーブルにエンティティが追加されます。
ORMの裏側で実行されたSQLは次の通りになります:
-- persitAndFlushの操作
BEGIN;
insert into "book" ("title")
values ('サバイバルTypeScript') returning "id";
COMMIT;
-- findAllの操作
select "e0".* from "book" as "e0";
以上でチュートリアルは完了です。このチュートリアルで作成したコードはGitHubにホスティングしてあります。