DockerでNode.js+PostgreSQLの環境を構築する

# はじめに
Docker上でNode.js(Express)とPostgreSQLの環境を構築しようとしたのですが、正しく動作する簡潔な記事が見当たらなかったので、本記事を投稿します。
データベースの情報を取得するWebアプリケーションを作成し、実際にデータを追加してみて正しく動作することの確認を行っていきます。

# 環境概要
**Webサーバー (Node.js)**
image : node:15-slim
**データベース (PostgreSQL)**
image  : postgres:13

# プロジェクト
## ディレクトリ構成
```
project/
  ├─app/                  # Node.jsアプリケーション
  │  ├─node_modules/
  │  ├─Dockerfile
  │  ├─index.js
  │  ├─package.json
  |  └─yarn.lock
  ├─initdb/               # PostgreSQL初期化ファイル
  │   └─setup.sql
  └─docker-compose.yaml 
```

## docker-compose
下記のように記載します。
PostgreSQLのデータはdb_dataボリュームで永続化しています。

```yaml:docker-compose.yaml
version: "3"
services:
  node:
    container_name: node_express
    build: ./app
    volumes:
      - ./app:/app
    tty:  true
    ports:
      - 3000:3000
  
  postgres:
    container_name: postgres
    image: postgres:13
    ports:
      - 5432:5432
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
      POSTGRES_DB: postgres_db
    volumes:
      - db_data:/var/lib/posrgresql/data
      - ./initdb:/docker-entrypoint-initdb.d

volumes:
  db_data: {}
```

## app：Node.jsアプリケーション
Expressを使用して、PostgreSQLのデータを取得するWebアプリケーションを作成します。

```javascript:index.js
const express = require('express');
const app = express();

// PostgreSQLの設定
const { Pool } = require('pg')
const pool = new Pool({
  user: 'postgres',
  host: 'postgres',
  database: 'postgres_db',
  password: 'password',
  port: 5432,
})

// ルーティングの設定
app.get('/', async(req, res) => {
  const { rows } = await pool.query('select * from users')
  res.send(rows)
});

const port = 3000;

app.listen(port, () => {
    console.log(`server started on port ${port}`);
}); 
```

pacakge.jsonは下記のように手書きするか、npm initなどで作成します。

```json:package.json
{
  "name": "app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "start": "node index.js"
  },
  "author": "",
  "license": "ISC",
  "dependencies": {
    "express": "^4.17.1",
    "pg": "^8.5.1"
  }
}
```

Dockerfileを下記のように作成し、ビルド時に必要なパッケージをインストールして起動するようにします。

```docker:Dockerfile
FROM node:15-slim

RUN yarn install

WORKDIR /app

CMD ["yarn", "start"]
```

## initdb：PostgreSQL初期化ファイル
postgresイメージでは、初期化時に`docker-entrypoint-initdb.d`内のsqlファイルが実行されるため、ここで初期化を行います。
Node.jsアプリケーション内でも記載していますが、usersというテーブルをこのファイル内で作成します。

```sql:setup.sql
set client_encoding = 'UTF8';

create table users(
    id serial primary key,
    name varchar not null,
    age integer not null
)
```

# 動作確認
## コンテナの立ち上げ
docker-composeコマンドでコンテナを立ち上げます。

```
docker-compose up
```

http://localhost:3000 にブラウザでアクセスすると、空のデータが取得できていると思います。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556091/9eab9f4e-8747-9956-2468-f913238aa756.png)

## データの追加
postgresコンテナに入ってデータを追加してみましょう。
下記コマンドで実行中のpostgresコンテナに入れます。

```
docker exec -i -t postgres bash
```

ターミナルに入れたら、psqlコマンドでPostgreSQLに接続します。

```
psql -U postgres -d postgres_db
```

この状態でSQLコマンドを入れれるようになるので、下記コマンドを実行します。

```
insert into users (name, age) values ('MY NAME', 20);
```
再度http://localhost:3000 にブラウザでアクセスすると、先ほど追加されたデータが帰ってきます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/556091/74e07f9f-2538-7600-d5e8-6900c5685139.png)

# おわりに
Docker上でNode.jsとPostgreSQLの環境を構築し、実際に動作確認を行いました。