TypeScript で ORM。TypeORM を使って PostgreSQL とやり取りしてみた
Node.js 上で使える O/R マッパーというと、以前 Sequelize というモノを紹介した。
コチラも MySQL や PostgreSQL など、色々な DB と接続できるマッパーだった。
今回見つけた TypeORM という O/R マッパーは、Nest.js にも採用されており、TypeScript の特性をフルに生かして実装できてとても便利なので、いくつか機能を紹介してみる。
目次
- Docker-Compose で PostgreSQL 環境を用意する
- TypeORM プロジェクトを作る
- 接続情報ファイルを作成する
- エンティティを作成する
- DB 接続・操作を行うエントリポイントを作成する
- 実行してみる
- 今回はココまで
Docker-Compose で PostgreSQL 環境を用意する
まずはとりあえず TypeORM を使ってみるため、Docker-Compose を使って PostgreSQL DB を立ち上げてみる。
Dockerfile
FROM postgres:11-alpine
ENV LANG ja_JP.utf8
docker-compose.yaml
version: '3'
services:
db:
build: .
ports:
- 5432:5432
environment:
POSTGRES_USER: admin
POSTGRES_PASSWORD: admin
これら2つのファイルを用意して
$ docker-compose up -d
でコンテナを起動し、
$ docker-compose exec db psql -U admin
このように叩くと、PostgreSQL の内部に入れる。
admin=# \l
List of databases
Name | Owner | Encoding | Collate | Ctype | Access privileges
-----------+-------+----------+------------+------------+-------------------
admin | admin | UTF8 | ja_JP.utf8 | ja_JP.utf8 |
postgres | admin | UTF8 | ja_JP.utf8 | ja_JP.utf8 |
template0 | admin | UTF8 | ja_JP.utf8 | ja_JP.utf8 | =c/admin +
| | | | | admin=CTc/admin
template1 | admin | UTF8 | ja_JP.utf8 | ja_JP.utf8 | =c/admin +
| | | | | admin=CTc/admin
こんな感じで、DB 一覧を確認できるはずだ。
ports
指定によりホスト側に 5432
ポートで公開してあるので、ホスト側に psql
コマンドがあれば
$ PGPASSWORD=admin psql -h localhost -p 5432 -U admin
こんな感じでも DB 接続できる。
TypeORM プロジェクトを作る
次に、TypeORM を叩くためのプロジェクトを作る。
$ npm init -y
$ npm install --save-dev typescript ts-node
# TypeORM と、PostgreSQL 接続用の pg パッケージをインストールする
$ npm install --save typeorm pg
# tsconfig.json を生成する
$ npx tsc --init
tsconfig.json
を開き、compilerOptions
配下に experimentalDecorators
と lib
プロパティを追加する。
{
"compilerOptions": {
// ↓ コレを追加
"experimentalDecorators": true,
"lib": [
"esnext",
"dom"
],
// あとはそのまま…
"module": "commonjs"
// ……
}
}
接続情報ファイルを作成する
プロジェクト雛形ができたら、DB との接続情報を .ts
ファイルに記述する。
db-config.ts
import { PostgresConnectionOptions } from 'typeorm/driver/postgres/PostgresConnectionOptions';
const dbConfig: PostgresConnectionOptions = {
type: 'postgres',
host: 'localhost',
port: 5432,
username: 'admin',
password: 'admin',
database: 'admin',
synchronize: true,
logging: false,
entities: ['entities/*.ts']
};
export default dbConfig;
実際のアプリでは 'localhost'
や 'admin'
などとベタ書きはせず、process.env
(環境変数) で注入してやることになるだろう。
エンティティを作成する
続いて、テーブル定義とレコードを表現する Entity クラスを作成する。
ココが TypeORM の真骨頂。デコレータを使ってテーブルの型や特徴を決められると同時に、フィールドの型定義にも適用されるのだ。
なにはともあれ、次のようにファイルを作ってみよう。
entities/customer.ts
import { Column, Entity, PrimaryColumn, PrimaryGeneratedColumn } from 'typeorm';
@Entity()
export default class Customer {
@PrimaryGeneratedColumn()
public id!: number;
@PrimaryColumn({ type: 'varchar', length: 10 })
public name: string;
@Column({ type: 'int' })
public age: number = 0;
constructor(name: string) {
this.name = name;
}
}
なんとなく、何をどう指定しているのかは推測が付くだろう。
DB 接続・操作を行うエントリポイントを作成する
最後に、DB 接続と操作を行うエントリポイントを作ってみる。
exec.ts
import { createConnection, getRepository } from 'typeorm';
import dbConfig from './db-config';
import Customer from './entities/customer';
createConnection(dbConfig).then(async (connection) => {
console.log('Postgres Connected');
try {
const customerRepository = getRepository(Customer);
const allCustomers = await customerRepository.find();
console.log('Select : ', allCustomers);
}
catch(error) {
console.error('Failed : ', error);
}
finally {
await connection.close();
console.log('Connection Closed');
}
})
.catch((error) => {
console.error('Postgres Connection Failed', error);
});
実行してみる
コレでコーディングは完了。いよいよ実行してみる。
まずは DB 接続して、SELECT * FROM customer;
相当の SQL を投げているだけ。実行してみると、次のようになるだろう。
$ npx ts-node exec.ts
Postgres Connected
Select : []
Connection Closed
PostgreSQL の方はテーブル作成などはしていないが、db-config.ts
内の synchronize: true
指定によって、自動的に Entity に対応するテーブルが作られている。
$ PGPASSWORD=admin psql -h localhost -p 5432 -U admin
# 以下のようにテーブルが作成されている
admin=# SELECT * FROM customer;
id | name | age
----+------+-----
(0 rows)
さらに exec.ts
に追記して、テーブルにデータを投入してみよう。
const customerRepository = getRepository(Customer);
// 以下を追加
const newCustomer = new Customer('Neo');
newCustomer.age = 30;
const savedCustomer = await customerRepository.save(newCustomer);
console.log('Saved : ', savedCustomer);
// 追加ココまで
const allCustomers = await customerRepository.find();
実行してみるとこんな感じ。
$ npx ts-node exec.ts
Postgres Connected
Saved : Customer { age: 30, name: 'Neo', id: 1 }
Select : [ Customer { age: 30, name: 'Neo', id: 1 } ]
Connection Closed
登録したデータには ID が自動採番されていることが分かる。
save()
メソッドは Upsert として動作するので、Insert と Update のどっちでも良い時に使える。Rails の ActiveRecord みたいなノリだ。
なお、今回は id
を自動採番させているので、このまま再実行するとどんどん同じようなデータが Insert されてしまうことに留意。
# スクリプトは変えず再実行
$ npx ts-node exec.ts
Postgres Connected
Saved : Customer { age: 30, name: 'Neo', id: 2 }
Select : [
Customer { age: 30, name: 'Neo', id: 1 },
Customer { age: 30, name: 'Neo', id: 2 }
]
Connection Closed
今回はココまで
TypeScript のデコレータを大活用している TypeORM。今回はその機能の一部しか紹介しきれなかったが、他にも DB マイグレーションファイルの生成やトランザクション処理など、様々なことができるのでオススメだ。