Node.js x HerokuでWebアプリを作った

# はじめに
『[存在しないクイズ](https://sonzaisinai.herokuapp.com/ "存在しないクイズ")』
というアプリを作りました。

▼URL
https://sonzaisinai.herokuapp.com

この世界に存在しないものを四択の中から選ぶクイズアプリです。
同じルームにいる人と対戦ができます。

<img width="400" alt="home" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632494/3c0d2cc8-f92b-b223-8972-43fee65c47c1.jpeg">
<img width="400" alt="game" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2632494/fbce6bb7-9a60-32c5-146b-6090899af17f.jpeg">

▼ソースコード
[GitHub](https://github.com/hirozouu/sonzaisinai.git "GitHub")

現在できるのはルーム対戦と問題の投稿です。
今後はランキングやランダム対戦などを追加していきます。

この記事ではSocket.ioのRoomsとHeroku Postgresに関して備忘録を残しておこうと思います。

# Node.jsについて
公式曰く「**非同期のイベント駆動のJavaScript環境**」
ざっくりと説明すると、サーバサイドをJavaScriptで記述できます。

今回はクライアント-サーバー間の通信のために**Socket.io**と呼ばれるライブラリを使用しました。

# Herokuについて
いわゆる「**PaaS**(Platform as a Service)」
200種類を超えるAdd-on(拡張機能)があり、GitHubと連携してアプリをデプロイすることもできます。

基本的には無料ですが
* 無料で使用できるリソースに制限がある
* 無料の場合30分アクセスがないとスリープする
* Gitがインストールされている必要がある
* add-onを使用するためには、クレジットカードを登録しなければならない

今回はインフラとデータベース(**Heroku Postgres**)を使用しました。

# 備忘録

## Socket.ioの双方向通信
**Socket.io**はクライアント-サーバ間の双方向の通信を実現するためのNode.jsライブラリです。

ざっくりとよく使うものをまとめました。

```javascript:server.js
// モジュールを読み込む
const express = require('express');
const http = require('http');
const socketIO = require('socket.io');

// サーバを構築する
const app = express();
const server = http.Server(app);
const io = socketIO(server);
```

イベントハンドラを設定します。
端的に言うと、"event"を受信したときに内側の処理が実行されます。

```javascript:sample.js
// イベント("event")とデータ(param)を受信する
socket.on("event", (param) => {
    conosle.log("Hello World");
});
```

### サーバ側
サーバは**socket.id**という一意のIDでクライアントを識別します。

```javascript:server.js
io.on('connection', (socket) =>
{
    // 全クライアントにイベントとデータを送信する
    io.emit("event", param);
    
    // 特定のクライアントにイベントとデータを送信する
    io.to(socket.id).emit("event", param);
}
```
### サーバ側(Rooms)
Socket.ioの**Rooms**を使うことで、ルーム機能を簡単に作ることができます。

```javascript:server.js
io.on('connection', (socket) =>
{
    // クライアントをルーム(strRoomName)に参加させる
    socket.join(strRoomName);

    // ルーム内の全クライアントにイベントとデータを送信する
    io.to(strRoomName).emit("event", param);

    // クライアントをルームから退出させる
    // 参加者がいなくなればルームは削除される
    socket.leave(strRoomName);
}
```

### クライアント側
```javascript:client.js
// サーバと接続する
const socket = io.connect();

// サーバにイベントとデータを送信する
socket.emit("event", param);

// サーバから切断する
socket.disconnect();
```

## Heroku Postgres
**Heroku Postgres**とは

* Herokuのデータベース
* Add-onsとして簡単に導入できる
* 無料では10,000行・1GBまでリソースを使用できる

### データベースにアクセスする

Node.jsは基本的に**非同期処理**です。
例えば、サーバがデータベースにアクセスして、取得したデータをクライアントに送信する場合には

1. サーバがデータベースにアクセスする
2. データをクライアントに送信する
3. データベースがデータをサーバに渡す

というようなことになり、結果として空のオブジェクトがクライアントに渡ることも起こり得ます。
そのため、データベースからデータを取得する場合には同期処理を使います。

```javascript:postgres.js
const { Client } = require('pg');

// table内の全データを取得する
const selectAll = async () =>
{
    const client = new Client(
        {
            connectionString: process.env.DATABASE_URL, 
            ssl: 
            {
                rejectUnauthorized: false
            }
        }
    );
    try
    {
        // client.connect()の結果が返されるまで待機する
        await client.connect();
        // client.query()の結果が返されるまで待機する
        const result = await client.query(
            "SELECT * FROM table");
        return result.rows;
    }
    catch(err)
    {
        console.log(err.stack);
    }
    finally
    {
        // client.end()の結果が返されるまで待機する。
        await client.end();
    }
}
```

# おわりに
▼お世話になりました

* [オンライン対戦ゲームを作る(Node.js + Socket.io)](https://www.hiramine.com/programming/onlinebattletanks_nodejs_socketio/index.html "オンライン対戦ゲームを作る(Node.js + Socket.io)")
* [Socket.ioのよく使う関数とか](https://qiita.com/uranesu/items/8ee0dbe4e472f9fffa49 "Socket.ioのよく使う関数とか")