0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Node.js/Express/routing-controllers+TypeScript+TypeORM+PostgreSQLでWeb API作成

Posted at

はじめに

Node.js/Express/routing-controllers+TypeScriptでWeb API作成では、routing-controllers を用いてより構造的なプログラムでシンプルな Web API を作成した。本記事では、TypeORM を用いることで PostgreSQL と連携し、DB テーブルから取得したデータを返す Web API を作成する。TypeORM はリレーショナル DB をサポートしており、TypeScript との相性がよいなど、複数のメリットがあるとのこと。実行環境は以下。

  • Node.js: 14.17.6
  • npm: 6.14.15
  • Express: 4.17.1
  • TypeScript: 4.4.3
  • routing-controllers: 0.9.0
  • typeorm: 0.2.37

TypeORM のインストールおよび tsconfig.json の設定

TypeORM にしたがって、インストールする。

$ npm install typeorm
$ npm install reflect-metadata
$ npm install pg
$ npm install -g typeorm

tsconfig.json についても、ドキュメントにしたがって設定する。以降のコードではデコレータを使用しているのだが、TSConfig Reference によるとデコレータは JavaScript ではまだ承認されていない機能であるため、以下設定が必要なようだ。

tsconfig.json
{
  ...
  "emitDecoratorMetadata": true,
  "experimentalDecorators": true,
  ...
}

TypeORM の使用方法

ドキュメントに沿って順に進める。Quick Start としてプロジェクトを一気に作成することもできるようだが、ここでは一つずつファイルを作成して進める。ディレクトリ構成は以下のようになっている。

.
├── ormconfig.json              # connection 設定を定義
├── package-lock.json
├── package.json
├── src
│   ├── app.ts                  # connection 作成・実行
│   ├── controllers             # route と handler を定義
│   │   └── photoController.ts
│   ├── entity                  # entity を定義
│   │   └── Photo.ts
│   └── migration               # migration ファイル置き場、omrconfig.json で指定
└── tsconfig.json

エンティティの作成

エンティティはモデルを @Entity デコレータで修飾したもので、このモデルに基づいた DB テーブルを作成できる。またデータの追加・削除などの操作も行うことができる。コードは以下。

src/entity/Photo.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Photo {

    @PrimaryGeneratedColumn()
    id!: number;

    @Column({
        length: 100
    })
    name!: string;

    @Column('text')
    description!: string;

    @Column()
    filename!: string;

    @Column()
    views!: number;

    @Column()
    isPublished!: boolean;
}

以下で上記コードの簡単な説明を行う。各デコレータの詳細はこちら

  • @Entity : モデルをエンティティ化するデコレータ。オプションでテーブル名を指定可で、デフォルトテーブル名はクラス名(小文字)を使用。
  • @Column : カラムを指定するデコレータ。型指定や文字数制限といったオプションが使える。
  • @PrimaryGeneratedColumn : プライマリカラムを指定するデコレータ。値は自動生成される。

またドキュメントには書かれていないが、上記では各プロパティに明確な割り当てアサーション ! を使用している。これは Property 'id' has no initializer and is not definitely assigned in the constructor. のようなエラーを起こさないためである。

コントローラの作成

上記で作成したエンティティを操作できるレポジトリを扱う。以下コードは、レポジトリがもつ標準メソッドを使用してテーブルの情報を取得するようなハンドラを定義している。async/await を使って、非同期処理もすっきり書ける。

src/controllers/photoController.ts
import { Get, JsonController } from 'routing-controllers';
import { getRepository } from 'typeorm';
import { Photo } from '../entity/Photo';

@JsonController()
export class PhotoController {
  private photoRepository = getRepository(Photo);

  @Get('/photos')
  async getAll() {
    const photos = await this.photoRepository.find();

    return photos;
  }
}

Node.js/Express/routing-controllers+TypeScriptでWeb API作成にて、routing-controllers の説明は行っているため、ここでは typeorm のレポジトリのみ説明する。

  • getRepository() : 指定したエンティティのレポジトリにアクセス。
  • someRepository.find() : レポジトリの標準メソッドの一つ。詳細はこちら

標準メソッドでは扱えないような複雑な処理をする場合は QueryBuilder() を用いるらしいが、基本的には標準メソッドのみで十分処理ができそう。

コネクションの設定

ormconfig.json にて、接続する DB の情報やオプションを設定する。このファイルは package.json と同階層に置くこと。コードは以下。

ormconfig.json
{
  "type": "postgres",
  "host": "172.17.0.1",
  "port": 5432,
  "username": "postgres",
  "password": "postgres",
  "database": "test_db",
  "entities": [
    "src/entity/*.ts"
  ],
  "migrations": ["src/migration/*.ts"],
  "cli": {
    "entitiesDir": "src/entity",
    "migrationsDir": "src/migration"
  },
  "synchronize": true
}

今回は TypeScriptでDocker上に構築したPostgreSQLに接続にて作成した DB に接続している。各種オプションの詳細はこちらを参照することとし、ここでは以下2点のみ説明する。

  • entities : connection に使用するエンティティを指定。
  • synchronize : アプリケーションを起動するたびにデータベーススキーマを自動作成する必要があるかどうかを指定。本番環境では非推奨。

これら設定をしているため、後述の実行確認ではアプリケーションを起動すると、src/entity/Photo.ts をもとに自動的にテーブルが作成される。

コネクションの作成

上記で設定した接続情報をもとに、コネクションの作成を行う。

src/app.ts
import 'reflect-metadata';
import { createExpressServer } from 'routing-controllers';
import { createConnection } from 'typeorm';
import { PhotoController } from './controllers/photoController';

createConnection().then(() => {
  const app = createExpressServer({
    controllers: [PhotoController]
  });
  const port = 3000;

  app.listen(port, () => {
    console.log('app listening');
  });
})

動作確認

上記で作成したアプリケーションが正しく動作するか確認する。アプリケーションの実行は以下コマンド。

$ npx ts-node src/app.ts
app listening

テーブルの自動作成

ormconfig.json にて "entities": ["src/entity/*.ts"]"synchronize": true としているため、まずはアプリケーション実行後にテーブルが自動作成されるか psql にて確認する。

# 別ターミナルにて
$ psql -h 127.0.0.1 -p 5432 -U postgres test_db

以下コマンドで、photo テーブルが作成されていること、作成したエンティティ通りのスキーマになっていることがわかる。

test_db=# \dt
         List of relations
 Schema | Name  | Type  |  Owner
--------+-------+-------+----------
 public | photo | table | postgres
(1 rows)

test_db=# \d photo
                                      Table "public.photo"
   Column    |          Type          | Collation | Nullable |              Default
-------------+------------------------+-----------+----------+-----------------------------------
 id          | integer                |           | not null | nextval('photo_id_seq'::regclass)
 name        | character varying(100) |           | not null |
 description | text                   |           | not null |
 filename    | character varying      |           | not null |
 views       | integer                |           | not null |
 isPublished | boolean                |           | not null |
Indexes:
    "PK_723fa50bf70dcfd06fb5a44d4ff" PRIMARY KEY, btree (id)

データの取得

テーブルの作成は確認できたため、続いて HTTP リクエストを送って正しくデータが返ってくるかを確認する。現状テーブル内は空なので、先ほどアクセスしたテーブルにデータを追加する。(データの追加も TypeORM で行えるが、ここは psql コマンドで行う。)

test_db=# insert into photo values
(1, 'A', 'Sea',      'sea_photo',     100, true),
(2, 'B', 'River',    'river_photo',    10, false),
(3, 'C', 'Mountain', 'mountain_photo', 50, true);
INSERT 0 3

test_db=# select * from photo;
 id | name | description |    filename    | views | isPublished
----+------+-------------+----------------+-------+-------------
  1 | A    | Sea         | sea_photo      |   100 | t
  2 | B    | River       | river_photo    |    10 | f
  3 | C    | Mountain    | mountain_photo |    50 | t
(3 rows)

$ curl http://localhost:3000/photos/ を実行すると、上記で登録したデータが返ってくることが確認できる。

おわりに

Node.js/Express+TypeScriptでWeb API作成Node.js/Express/routing-controllers+TypeScriptでWeb API作成に続いて、Node.js/Express/routing-controllers+TypeScript+TypeORM+PostgreSQL で概ねやりたいことは実現できた。
ただ追加で調べていたところ、NestJS なるフレームワークを発見。TypeScript 製のバックエンドフレームワークで、Express をコアとして動作し、routing-controllers のようにデコレータを使いながらコントローラクラスを設計することができ、さらに Angular の思想に則ったモジュールアーキテクチャが採用されているらしく、その他にも利点多数。
フロントエンドで Angular を触っている身からすると、NestJS が Node.js/Express/routing-controllers の上位互換であるように見える。。。
ということで、NestJS+TypeORM+PostgreSQL という構成でも試してみたい。(NestJS は結構メジャーっぽいのになんで先に見つけられなかったんだ。。。)

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?