MySQLで取得したデータを、EJSでWebブラウザに出力する

# はじめに

こちらは、[エンジニアの新たな学びキャンペーン](https://qiita.com/official-events/716dbcb617afc83e9555)に向けた記事となります。

[Node.js + Express で作る Webアプリケーション 実践講座](https://www.udemy.com/course/web-application-with-nodejs-express/)を参考にしながら、
データベース（以下DB）内のデータを、Webブラウザに表示する方法を記事にしました。

なお、ここではNode.jsのテンプレートエンジンであるEJSと、RDB（リレーショナルデータベース）であるMariaDB(MySQL)を利用します。

(上記講座ではMongoDBが利用されていますが、本記事ではMySQLに置き換えました。)

# 実行環境

- Node.js v12.16.3
- Express 4.16.1
- 10.4.11-MariaDB

# 対象者

- JavaScriptの文法自体は学んだけど、Web技術はまだほとんど学べていない人
- かんたんなCRUD操作に関するSQLを理解している人

# 本記事でわかること
- サーバーサイド言語Node.js & フレームワークExpressを使い、Hello Worldする方法
- ExpressとMariaDB（MySQL）の連携方法
- **ExpressとMariaDB（MySQL）を使い、DB内のデータをWebブラウザ上に出力する方法**

# 対象のUdemy講座で学んだこと

対象の講座で学んだことのうち、特に本記事へ反映する内容は以下となります。

- パッケージマネージャであるnpmを使い、ミドルウェアやフレームワークを導入する方法
- EJSの構文の使い方
- Expressでのルーティング方法（特に関数の引数に関して）

# EJSとは

EJSは、テンプレートエンジンと呼ばれるもののひとつで、
テンプレートエンジンはHTMLの中にプログラム言語を埋め込むことができます。

特にEJSにおいては、HTML文書の中に`<% %>`, `<%= %>`タグなどを埋め込み、この中にプログラムを記述します。

### EJSの基本構文

[EJSの基本的な書き方](https://qiita.com/miwashutaro0611/items/36910f2d784ff70a527d)にきれいにまとまっていたので、
こちらを参照すると幸せになれます。


### EJSの利点

EJSは、サーバーサイドで保持している変数の値を併用してHTMLを記述するとき、書きやすさ・読みやすさの点で非常に強力です。

例えばサーバーサイド言語のみでHTML文書を書く場合、次のようなソースコードになります。

```app.js
const express = require("express");
const app = express();

app.get("/", (req, res) => {
  const text = "Hello World";
  let data = "<!DOCTYPE html>\r\n";
  data += "<html>\r\n";
  data += "  <head>\r\n";
  data += "    <meta charset='UTF-8'>\r\n";
  data += "    <title>hoge</title>\r\n";
  data += "  </head>\r\n";
  data += "  <body>\r\n";
  data += "<p>";
  data += text
  data += "</p>\r\n";
  data += "  </body>\r\n";
  data += "</html>";
  res.send(data);
});
app.listen(3000);
```

一方で、テンプレートエンジンを併用した場合は、次のようになります。
クォーテーションや改行を意味する`\r\n`などが消え、読み書きしやすくなっているのが分かります。

```app.js
const express = require("express");
const app = express();
app.set("view engine", "ejs");

app.get("/", (req, res) => {
  const text = "Hello World";
  res.render("index", {text});
});
app.listen(3000);
```

```index.ejs
<!DOCTYPE html>
<html>
  <head>
    <meta charset='UTF-8'>
    <title>hoge</title>
  </head>
  <body>
    <p><%= text %></p>
  </body>
</html>
```


# 実行の準備

さて、まずは環境の構築を行います。
Node.js, MariaDB(MySQL)はインストールされているものとします。

## サーバーサイドの準備

以下のコマンドを**順に実行して**、フレームワークやミドルウェアを導入します。


- `$ npm init`
- `$ npm install express --save`,
- `$ npm install mysql`
- `$ npm install ejs -- save`


## MariaDB（MySQL）の準備

以下のSQLを**順に実行**します。
この操作により、Webサイトの名前・URLに関するテーブルを作成し、データの追加も行います。

- `create database website`
- `create table website(name varchar(255), url varchar(255));`
- `insert into website(name, url) values ("google", "https://www.google.com/"), ("amazon", "https://www.amazon.co.jp/"), ("apple", "https://www.apple.com/"), ("facebook", "https://www.facebook.com/");`

# 実装する

次のような順序で、簡単なことから実装していきます。

1. EJSを使い、Webブラウザ上でHello Worldする
2. DB接続を行いデータを取り出し、ターミナル上にデータを出力する
3. DB接続を行い、EJSファイルを利用してWebブラウザ上にデータを出力する（ここでは一度失敗してみる）
4. DB接続を行い、EJSファイルを利用してWebブラウザ上にデータを出力する（再チャレンジし、成功する）

## 1. EJSでHello Worldしてみよう

まずはDBのことは一旦 忘れて、EJSを使ってHello Worldをしてみます。

下記ソースコードを保存後、ターミナル上で`$ node app.js`と入力し、
Webブラウザで`http://localhost:3000/`にアクセスします。

次の画像のように表示されたら成功です。
ちなみに、
`app.js`のソースコード内の`app.get()`の第一引数はリクエストURL、
第二引数はリクエストが送られたときに実行されるコールバック関数を指します。

この処理を口語的に説明するなら、
`http://localhost:3000/`が呼ばれたら次のコールバック関数を実行してね！
そしてそのコールバック関数には、index.ejsを表示して！っていう命令も含まれてるよ！
といったところでしょうか。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549712/b5084807-f211-d27c-0cad-3a25d878dc15.png)


### 実行するソースコード

```app.js
const express = require("express");
const app = express();
app.set("view engine", "ejs");

app.get("/", (req, res)=>{
    res.render("index.ejs");  // デフォルトでは /viewsからの相対パスで表すので注意
})

app.listen(3000);

```

```views/index.ejs
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello World</h1>
  </body>
</html>
```




## 2. DBに接続してみよう

次に、DB内のデータをターミナル上で表示する実装を行います。

`app.js`を次のように書き換えます。
なお、`mysql.createConnection()`の各種ユーザ情報は、必要に応じて書きかえてください。

### 実行するソースコード

```app.js
const express = require("express");
const app = express();
const mysql = require("mysql");
const connection = mysql.createConnection({
    host: "localhost",
    user: "root",
    password: "1234",
    database: "website"
});
app.set("view engine", "ejs");

app.get("/", (req, res)=>{
    const sql = "select * from website";
    connection.query(sql, (err, result, fields)=>{
        if(err)throw err;
        console.log(result);
    })
    res.render("index.ejs");  // デフォルトでは "/views"ディレクトリからの相対パスで表すので注意
})

app.listen(3000);
```


ターミナル上で`$ node app.js`と入力し、Webブラウザで`http://localhost:3000/`にアクセスします。
前回と同様にHello WorldがWebブラウザに表示されており、
更にターミナル上に、次のような表示があれば成功です。

ここでは`console.log(result)`の実行による、ターミナルのデータ出力結果から、
変数`result`にはDBから取り出したデータが入っていることが確認できます。

```terminal
$ node app.js
[
  RowDataPacket { name: 'google', url: 'https://www.google.com/' },
  RowDataPacket { name: 'amazon', url: 'https://www.amazon.co.jp/' },
  RowDataPacket { name: 'apple', url: 'https://www.apple.com/' },
  RowDataPacket { name: 'facebook', url: 'https://www.facebook.com/' }
]
```

## 3. DB内のデータをWebブラウザに表示してみる

『1.EJSでHello Worldしてみよう』, 『2.DBに接続してみよう』では、
`res.render("index.ejs")`とレンダリング先を表記し、ルーティングを設定していました。

今回はレンダリング先にデータを渡すために、
`app.get()`内にレンダリング先だけではなく、DBから取得したデータも記述する必要があります。
そこで`res.render()`の第二引数に、レンダリング先に送るデータを記述します。

より具体的にはいえば、
『2. DBに接続してみよう』で、変数`result`にDBのデータが格納されることが確認できていました。
このresultを`res.render()`の第二引数に指定します。
従って、ここでは`res.render("index", { web: result})`と記述します。

ところで`{ web: result}`という記述に、ややこしさを感じるかもしれません。
これは**resultからwebへ名前を置換してから、データを送る**という処理を含んでいます。

EJSに対して、resultという変数名をそのままに渡してしまうと、
「result？結果？いや何の結果を表す変数なのか、なんのこっちゃわからん」と、
フロントエンドエンジニアが困惑することになってしまいます。

（webという変数名ならば適切なのかという問題はさておき。）

### 実行するソースコード

DBに保存しているWebサイト名やURLをWebブラウザ上に出力するため、
`app.js`と`index.ejs`を、それぞれ次のように書き換えます。

```app.js
const express = require("express");
const app = express();
const mysql = require("mysql");
const connection = mysql.createConnection({
    host: "localhost",
    user: "root",
    password: "1234",
    database: "website"
});
app.set("view engine", "ejs");

app.get("/", (req, res)=>{
    const sql = "select * from website";
    connection.query(sql, (err, result, fields)=>{
        if(err)throw err;
        console.log(result);
        res.render("index", { web: result});
    })
})

app.listen(3000);
```

```/views/index.ejs
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello World</h1>
    <%= web %>
  </body>
</html>
```

ターミナルに`node app.js`を入力し、Webブラウザで`http://localhost:3000/`にアクセスします。
次の画像のような、Hello Worldと**Objectという文字の羅列**が確認できるでしょうか？
**半分だけ成功です！**

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549712/6a4719da-66e2-1eba-63b3-4b529a7c1199.png)

本来であれば`Google`や`Apple`といったWebサイト名や、
`https://www.google.com/`のようなURLがほしかったところですが、
DBから何らかのデータを取り出すことには、ひとまず成功したのではないでしょうか。
`[]`で囲まれたよくわからないものは4つで、DBに登録したレコードもちょうど4つでしたしね。

次の項で、この問題を解決します。


## 4. DB内のデータをWebブラウザに表示してみる（再挑戦）



前項ではDBから、どうやら何らかのデータを取り出すことには成功しましたが、
Webサイトの名前やURLを取得することはできませんでした。

この原因について考えます。

`http://localhost:3000/`にアクセスしたとき、
`console.log(result)`の実行によって、ターミナルに何か表示されたことは覚えているでしょうか？

それは、次のような内容でした。

```terminal
$ node app.js
[
  RowDataPacket { name: 'google', url: 'https://www.google.com/' },
  RowDataPacket { name: 'amazon', url: 'https://www.amazon.co.jp/' },
  RowDataPacket { name: 'apple', url: 'https://www.apple.com/' },
  RowDataPacket { name: 'facebook', url: 'https://www.facebook.com/' }
]
```


一見するとオブジェクトですが、
出力されたデータは`[]`で囲まれているため、**これはオブジェクトたちを格納している『配列』です**。

従って、例えば`google`を取得する場合は`web[0]["name"]`と記述します。

### 実行するソースコード

例として、`google`と`https://www.google.com/`をWebブラウザ上に表示してみます。
次のように`index.ejs`を書き換え、再度 `$ node app.js`で実行します。
下記画像のように、Webサイト名とURLが表示されたら、成功です。


```/views/index.ejs

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Document</title>
  </head>
  <body>
    <h1>Hello World</h1>
    <%= web[0]["name"] %>
    <br>
    <%= web[0]["url"] %>
  </body>
</html>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/549712/042bb24b-efb5-ccb1-c691-f6ab0abdd087.png)

この他にも応用として、
for文を利用するなどして、一度に複数のデータを出力すること、
あるいはSQLを変更してデータの更新・追加・削除することも可能です。



# おわりに
プログラミングを本格的に始めて1年も満たない未熟者の言葉ではありますが、
プログラミング言語の文法を修めるだけでは、Webのシステム開発は正直 不可能です。

私はJavaScriptの文法を学習した後、サーバーサイド言語としてのJavaScript(Node.js)に入門したのですが、
すぐにHTTPリクエスト、ルーティングなどといったWeb特有の専門用語に悩まされました。

Webアプリケーションのサーバーサイドへ入門する前に、
『この一冊で全部わかるWeb技術の基本』, 『Webを支える技術』, 『Web技術速習テキスト』といったWeb周りの情報に触れておくことを、強くおすすめします。




# 参考

- [Node.js + Express で作る Webアプリケーション 実践講座](https://www.udemy.com/course/web-application-with-nodejs-express/)
- [Express でのデータベースの統合](https://expressjs.com/ja/guide/database-integration.html)
- [EJSの基本的な書き方](https://qiita.com/miwashutaro0611/items/36910f2d784ff70a527d)