Lesson 5
DBとの連携
目次
Lesson 5
Chapter 1
mysqlモジュールのインストール
Lesson4では、Expressを用いたHTTPサーバーの構築を学びました。リクエストやレスポンスの処理を覚えたことで、本格的なWebアプリケーションへの道筋が見えてきたと思います。
続くLesson5では、Expressを用いたデータベース(DB)サーバーとの接続を学びます。DBとの連携もまた、今日のWebアプリケーションには欠かせないものです。
このLessonを通して、参照・追加・更新・削除といったDBの基本操作を身につけてください。
プロジェクトの準備
実践の前準備として、新規のプロジェクトを作成しましょう。任意の作業ディレクトリに移動し、以下のコマンドを入力します。
ターミナル(Mac / Linux / Windows共通)
$ mkdir express-mysql-app
$ cd express-mysql-app
$ npm init -y
$ npm install express ejs
プロジェクト直下に「package.json」ファイルが作成され、dependencies
の項目にExpressとEJSのバージョンが記されていることを確認してください。
続いて、「app.js」ファイルと「views」ディレクトリを作成します。「views」ディレクトリ配下には「index.ejs」ファイルを作成しましょう。
コマンドライン上では、以下の操作で作成できます。
ターミナル(Mac / Linux)
$ touch app.js
$ mkdir views
$ touch views/index.ejs
ターミナル(Windows)
$ New-item app.js
$ mkdir views
$ New-item views/index.ejs
念のため、以下の画像と同じディレクトリ構成であることを確認してください。
現在のディレクトリ構成
以上でプロジェクトの初期化は完了です。ここからプロジェクトを編集していきましょう。
プロジェクトの編集
続いて「app.js」および「index.ejs」のファイルを編集します。
app.js
先にExpressを記述しましょう。先ほど作成した「app.js」を以下のように編集します。現時点では、リクエストに対してページを表示するだけの単純な実装です。
app.js
const express = require('express')
const path = require('path')
const app = express()
const PORT = 3000
// ejs用の設定追加
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
app.get('/', (req, res) => {
res.render('index.ejs')
})
app.listen(PORT, () => {
console.log(`Starting server on PORT: ${PORT}`)
})
index.ejs
EJSも記述します。「index.ejs」を以下の通り編集してください。こちらも現時点では確認用のコードです。
index.ejs
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<h1>Hello EJS!</h1>
</body>
</html>
以上でプロジェクトの基本編集は完了です。それでは、実際にサーバーを起動してみましょう。
プロジェクトの起動
node app.js
を打ち込んでサーバーを起動します。
サーバーの起動
cd express-mysql-app
node app.js
サーバーが起動されたら、任意のブラウザから「https://localhost:3000」にアクセスしてください。「Hello EJS!」とページに表示されたら成功です。
mysqlモジュールのインストール
MySQLと接続するためには、ドライバーであるmysqlモジュールが必要です。
mysqlモジュールはExpressやEJSと同様に、npmを使ってインストールできます。
~/express-mysql-app
$ npm init mysql
MySQLとmysql
紛らわしいですが、以降「mysql」と書く場合はNode.jsのパッケージ名を指し、「MySQL」と書く場合はデータベース管理システム(DBMS)の名称を指すことにします。両者を混同しないように注意してください。
インストールが終わったら、「package.json」にあるdependencies
プロパティを参照してください。その中に「mysql」の項目があればインストール完了です。
まとめ
今回は「mysqlモジュール」のインストールを行いました。作成したプロジェクトは、次のチャプター「MySQLとの接続」で使用します。

Lesson 5
Chapter 2
MySQLとの接続
今回のChapterでは、ExpressとMySQLの接続方法について解説します。両者を接続することで、Express上から簡単にデータの取得や更新が行えるようになります。
一部SQLの操作を必要とする箇所もありますが、解説通り行えば知識がなくても問題ありません。順を追って学習を進めていきましょう。
DBとの接続にはMySQLのインストールが必要です。インストールしていない方はPROQの「MySQLのレッスン」などを参考に環境を構築してください。
また、前回のChapter1でmysqlパッケージをインストールしていない方も、忘れずに準備を済ませておきましょう。
DBの準備
初めに接続先のDBを作成します。以下に従って、コマンドラインからMySQLにログインしましょう。
ターミナル
$ mysql -u ユーザー名 -p
Enter password:
パスワードを聞かれるため、環境構築時に設定した自身のパスワードを打ち込んでください。
MySQLのコンソール画面
画像のようなコンソール画面に移行し、mysql>
と表示されたらログイン成功です。
DBの作成
それではDBの作成を進めていきます。念のためSHOW
コマンドを使用し、既存のDBがあるかどうか確認してください。
MySQL
mysql> SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| mysql |
| performance_schema |
+--------------------+
3 rows in set (0.00 sec)
次にCREATE
文を使用し、任意の名前でDBを作成します。ここではexpress_mysql_todo
としておきましょう。
MySQL
mysql> CREATE DATABASE express_mysql_todo;
Query OK, 1 row affected (0.00 sec)
再度SHOW
コマンドを入力し、DBの一覧を表示します。作成したDB(express_mysql_todo
)が存在すれば成功です。
MySQL
mysql > SHOW DATABASES;
+--------------------+
| Database |
+--------------------+
| information_schema |
| express_mysql_todo |
| mysql |
| performance_schema |
+--------------------+
テーブルの作成
続いてテーブルを作成します。USE
コマンドを使用し、先ほど作成したexpress_my_todo
を選択してください。
MySQL
mysql> USE express_mysql_todo;
Database changed
Database changed
と表示されたら成功です。続けてCREATE
文でテーブルを作成しましょう(テーブル定義については後述します)。
MySQL
mysql> CREATE TABLE todos (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, title VARCHAR(255));
Query OK, 0 rows affected (0.02 sec)
それではテーブルの一覧を見てみましょう。以下のようにテーブルが作成されていることが確認できたでしょうか。
MySQL
mysql> SHOW TABLES;
+------------------------------+
| Tables_in_express_mysql_todo |
+------------------------------+
| todos |
+------------------------------+
1 row in set (0.00 sec)
テーブルの中身も確認します。SHOW COLUMNS
コマンドを使って以下のように入力してください。
MySQL
mysql> SHOW COLUMNS FROM todos;
+-------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(255) | YES | | NULL | |
+-------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)
上記の通りテーブル定義が表示されていれば完了です。
テーブル定義
今回は単純なTodoアプリを想定し、「id」「title」という2つのカラムを用意しました。それぞれの説明は以下の通りです。
カラム | データ型 | 説明 |
---|---|---|
id | 整数型 | Todo項目のID。識別用に自動で割り振られる。 |
title | 文字列型 | Todo項目のタイトル。 |
データの挿入
INSERT
文を用いて、これから扱うテストデータを追加します。
MySQL
mysql> INSERT INTO todos (title) VALUES ('First Todo');
Query OK, 1 row affected (0.00 sec)
念のためSELECT
を使ってカラムの値を確認しましょう。
mysql> SELECT * FROM todos;
+----+------------+
| id | title |
+----+------------+
| 1 | First Todo |
+----+------------+
1 rows in set (0.00 sec)
以上でDBの準備が整いました。この「express_my_todos」に対して、HTTPサーバーの側から接続を試みます。
DBへの接続
今まではMySQLに対して直接クエリを打ち込んでいましたが、今度は同じ操作をExpressから行えるようにします。
やや複雑な記述が増えますが、ひとつひとつ理解した上で進めてください。
接続には、本章のChapter1でインストールしたmysqlモジュールを利用します。まずは必要なディレクトリとファイルを作成しましょう。
ターミナル(Mac / Linux)
$ cd express-mysql-todo
$ mkdir lib && cd lib
$ mkdir database && cd database
$ touch client.js && touch execute.js
画像と同じ構成になっていればOKです。
現在のデイレクトリ構成
mysqlクライアントの作成
まずは「mysqlクライアント」を作成します。 mysqlクライアントとは、DBへの接続情報を保持したオブジェクトです。DBへの操作を行うときは、このオブジェクトを参照してクエリを実行します。
「client.js」ファイルを開き、以下の通り記述してください。
プロパティ | 値 |
---|---|
host | localhost |
user | MySQLのユーザー名 |
password | MySQLのパスワード |
lib/database/client.js
const mysql = require('mysql')
const mysqlClient = mysql.createPool({
host: 'localhost',
user: 'admin',
password: 'password',
})
module.exports = mysqlClient
コードの解説は次の通りです。
require('mysql')
でmysqlモジュールを読み込んでいます。const client
にDBへの接続情報を保持したオブジェクトが格納されます。module.exports
で別ファイルにて読み込めるようにモジュール化しています。
モジュール化したclient
オブジェクトを外部ファイルから参照し、DBへのクエリを実行します。
クエリ実行用モジュールの作成
次に、クエリを実行するためのモジュールを作成します。このようにモジュールを分割することで、保守性の高いコードが記述可能です。
execute.js
const executeQuery = (pool, query, params = undefined) => {
return new Promise((resolve, reject) => {
pool.query(query, params, (err, results, fields) => {
if (err) reject(err)
resolve({ results, fields })
})
})
}
module.exports = executeQuery
引数は以下の3つを受け取ります。
引数 | 説明 |
---|---|
pool | MySQLの接続情報を保持したオブジェクト |
query | DBに対して実行するクエリ |
params | クエリの引数(WHERE句などに使用) |
「Promise」という言葉に戸惑った人も多いでしょう。簡単に説明すると、Promiseは非同期処理を同期的に処理するためのオブジェクトです。
そもそもJavaScriptでは、必ずしも記述した順番に処理が終わるわけではありません。上記の例におけるDBアクセスのように、時間のかかる処理は順不同で(非同期的に)終了する状況が頻繁に発生します。
非同期で複数の処理を進められるというのは、ネットワーク通信を行う上で非常に便利です。しかし、一方で処理が終わるタイミングを制御しづらいという欠点もあります。
Promiseはこの欠点を補い、データの取得やエラー処理を円滑に行うための仕組みです。
Lesson6-3では、Promiseを用いたエラーハンドリングについて学びます。非同期処理の仕組みもそこで詳しく触れることにしましょう。
クエリ実行
それでは、Express上からDBに対してクエリを実行しましょう。app.jsを以下のように編集してください。
app.js
const express = require('express')
const path = require('path')
const executeQuery = require('./lib/database/execute.js')
const mysqlClient = require('./lib/database/client.js')
const app = express()
const PORT = 3000
...省略
app.get('/', async (req, res) => {
try {
await executeQuery(mysqlClient, 'USE express_mysql_todo')
const { results } = await executeQuery(mysqlClient, 'SELECT * FROM todos')
console.log('The title is: ', results[0].title)
} catch (err) {
console.log(err)
}
})
...省略
こちらもすべての挙動を理解する必要はありません。簡単にコードを解説しましょう。
executeQuery
は前掲のモジュールです。ここでは、第1引数としてmysqlクライアント、第2引数として実行するクエリ文を指定しています。- todosテーブルから取得したデータはresultsに格納され、console.logにより出力されます。
ブラウザで「http://localhost:3000/」にアクセスしましょう。リクエストに対してクエリが実行された結果、下記画像のようにDBの取得データが表示されるはずです。
クエリ実行結果「The title is: First Todo」
以上で、今回の目的であったMySQLとの接続は完了しました。
まとめ
今回はMySQLとの接続を行いました。大事な部分を復習しておきましょう。
- MySQLへの接続は
mysqlモジュール
を使用する。 - Expressで
mysqlクライアント
のオブジェクトを作成し、MySQLへの接続情報を保持する。 - MySQLへのクエリは
mysqlクライアント
を介して実行する。
次のChapterでは、MySQLからデータを取得したり更新したりする「CRUD操作」を画面上で行います。徐々に内容が難しくなっていきますが、過去のChapterを振り返りつつ学習を進めていきましょう。

Lesson 5
Chapter 3
DBから取得したデータを表示する
Chapter2では、MySQLに接続してDBのデータを取得しました。今回のChapterでは、取得したデータを画面上に表示させてみましょう。
実装を通して、Webアプリケーションの基本概念である「CRUD」への理解を深めてください。
CRUDとは
「CRUD」は、データベースが備えるべき4つの機能をまとめた用語です。具体的にはCreate(生成)、Read(読み取り)、Update(更新)、Delete(削除)の頭文字を取って命名されました。
名前 | 操作 | 対応するSQL文 |
---|---|---|
Create | 生成 | INSERT |
Read | 読み取り | SELECT |
Update | 更新 | UPDATE |
Delete | 削除 | DELETE |
上記の表から分かるように、MySQLなどの一般的なデータベースにはCRUDの機能が備わっています。INSERT
やSELECT
は前回のチャプターでも使用しており、覚えている方も多いでしょう。
CRUDの原則はデータベースに限った話ではありません。DBを用いるWebアプリケーションもまた、上記の4要素を満たしている必要があります。
たとえば、商品の在庫管理システムを想像してください。きっと画面上には登録(Create)や検索(Read)、変更(Update)、削除(Delete)のボタンが並んでいるはずです。仮にどれか一つの機能でも欠けてしまえば、とてもアプリケーションとは呼べない代物になるでしょう。
CRUDの原則は当たり前のように思えるかもしれません。しかし、システムが複雑になるほどミスも生じやすくなります。「商品の登録機能は実装したが、顧客の連絡先情報が更新できない」などといった事態が起きないように、日頃からCRUDを意識した設計を心がけてください。
Todoアプリを作成する
ここからは実践として、簡単なTodoアプリを作ってみましょう。簡単といっても、アプリとして必要な機能を持たせるためには、先に述べたCRUDに対する理解が求められます。
テストデータのインサート
最初にテストデータをDBにインサートします。前回同様、コマンドラインからMySQLのコンソールにログインしてください。
ターミナル
mysql -u 自身のユーザー名 -p
Enter password: 自身のパスワード
MySQLのコンソールに移ったら、前回のチャプターで作成したexpress_mysql_todos
のDBを選択します。
ターミナル
USE express_mysql_todo;
Database changed
以下に従って、テストデータを挿入するためのクエリを入力します。
ターミナル
INSERT INTO todos (title) VALUES ('おにぎりを買う'), ('お茶を買う'), ('散歩に行く');
Query OK, 1 row affected (0.00 sec)
挿入用のクエリが実行されました。念のためテストデータが入っているか確認しましょう。
ターミナル
SELECT * FROM todos;
+----+----------------+
| id | title |
+----+----------------+
| 1 | First Todo |
| 2 | おにぎりを買う |
| 3 | お茶を買う |
| 4 | 散歩に行く |
+----+----------------+
4 rows in set (0.00 sec)
上記のように一覧表示されていれば、テストデータの準備は完了です。
DBから取得したデータを表示する
次に、挿入したテストデータをExpressで取得し、画面に表示してみましょう。Expressの取得処理は以下のように記述できます。
app.js
...省略
app.get('/', async (req, res) => {
try {
await executeQuery(mysqlClient, 'USE express_mysql_todo')
const { results } = await executeQuery(mysqlClient, 'SELECT * FROM todos')
// 変更箇所
console.log(results)
} catch (err) {
console.log(err)
}
})
...省略
編集が終わったら、コマンドラインにnode app.js
と打ち込んでサーバーを起動します。ブラウザから「http://localhost:3000/」にアクセスしてください。
画像のように複数のTODOが取得できたかと思います。
すべてのTODOを取得
viewファイルの編集
Experessを用いてDB上のデータを取得することができました。今度は、取得したデータをWebページに表示させてみましょう。
「index.ejs」を以下の通り編集します。
index.ejs
<body>
<h1>TODO アプリ</h1>
<ul>
<li></li>
</ul>
</body>
forEach構文を用いて、配列(todo)のデータをひとつずつ取り出しています。その中のtitle
を表示するため、EJSの記法が使われていることを確認してください。
レスポンスの作成
最後に「app.js」を修正します。Expressのres.render
メソッドによって、EJS
のテンプレートにデータが渡されていることを確認しましょう。
app.js
app.get('/', async (req, res) => {
try {
await executeQuery(mysqlClient, 'USE express_mysql_todo')
const { results } = await executeQuery(mysqlClient, 'SELECT * FROM todos')
res.render('index.ejs', { todos: results })
} catch (err) {
console.log(err)
}
})
書き換えが完了したらnode app.js
でサーバーを起動し、ブラウザで「http://localhost:3000/」に接続してください。画像のように表示されれば成功です。
まとめ
今回は取得したDBのデータを処理し、EJSを用いて画面に反映させました。これでCRUDにおける「Read(読み取り)」の機能が実装されたことになります。
残るChapterでは、追加(Create)と更新(Update)の実装を解説します。コードの記述量が増えていきますが、流し読みはせず、できるだけ理解に努めるようにしてください。

Lesson 5
Chapter 4
画面から入力した内容を登録する
Chapter3では、TodoアプリのデータをDBから取得し、Webページに表示する方法を学びました。今回のChapter4では、新たにデータの追加機能を実装します。CRUDの考え方に従えば、「Create(作成)」にあたる箇所です。
クライアントがデータの追加を要求する場合、一般に「POST」メソッドが使用されます。この機会に、Expressを用いたPOSTメソッドの受け取り方を学びましょう。
GETメソッドとPOSTメソッドの違い
「Expressを用いたリクエストデータの取得なら、Lesson4ですでに学んだはず」と思われるかもしれません。しかし、Lesson4で解説したのはGETメソッドによるパラメーターの取得でした。
ブラウザに「localhost:3000/todos/1」と入力したのを覚えていますか? GETメソッドでは、このように取得したい情報をURLの末尾に付け加えます。GETメソッドはWebページの閲覧など、サーバーのデータを取得したい場合に使われる通信方式です。
一方、入力フォームなどを介してサーバーにデータを送りたい場合には、原則としてPOSTメソッドが使われます。POSTメソッドの情報はURLに含まれず、「リクエストボディ」という形で送信されます。
HTTPメソッド | 説明 |
---|---|
GET | 指定したターゲットをサーバーから取得する |
POST | 指定したターゲットにデータを送る |
PUT | サーバーにファイルを書き込む |
DELETE | サーバーのファイルを削除する |
その他のHTTPメソッド
他にもHEAD(ターゲットのヘッダ情報を取得する)やPATCH(データを部分的に更新する)など様々なメソッドがあります。
リクエストの形式が異なる以上、これまでのapp.get
メソッドは使用できません。以下の項目で、リクエストボディを取得する方法を見ていきましょう。
【参考】リクエストボディとは
リクエストボディは、HTTPリクエストを構成する領域のひとつです。他の領域として、リクエストヘッダーとリクエストパラメーターがあります。
以下の表を参考に、HTTPリクエストの構成を整理しておきましょう。
構成名 | 説明 |
---|---|
リクエストパラメーター | HTTPメソッドやターゲット、HTTPのバージョン情報など。 |
リクエストヘッダー | 通信先のホストや認証、文字コード情報など。 |
リクエストボディ | 上記に含まれない長い文字列やパスワードなど。 |
HTTP通信では、すべてのデータが文字列として送受信されます。この文字列のうち、先頭行に置かれる情報が「リクエストパラメーター」です。具体的には、メソッド(GETやPOSTなど)とターゲット(URL)、HTTPのバージョン情報が格納されています。
続く複数行は「リクエストヘッダー」と呼ばれる領域です。具体的には、通信先のホストや認証、文字コード情報などが格納されています。
そして空白行を挟んだ最後の領域が「リクエストボディ」です。長い文字列やパスワードなどのURLに含めたくない情報は、このリクエストボディに格納されます。
リクエストボディ
リクエストボディは基本的にPOSTやPUTメソッドで用いられ、GETメソッドでは使用されません。
リクエストボディを受け取る
それでは、Express
によるリクエストボディの取得方法を見ていきましょう。手始めとして、「app.js」を以下のように編集してください。
app.js
...省略
// ejs用の設定追加
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
// express 設定 追加
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
...省略
上記のコードでは、Express
の内部機能であるexpress.json
と
express.urlencoded
という二つの関数をミドルウェアとして定義しています。
- express.json:リクエストボディ(JSON形式)を解析する
- express.urlencoded:エンコードされた文字列を解析する
body-parser
従来のExpressでは、リクエストボディの解析に外部モジュール(body-parser)が必要でした。最新版(v4.16.0以降)ではexpress.jsonとexpress.urlencodedが実装され、外部モジュールに依存しない設計が可能となっています。
URLに含まれるリクエストパラメータと異なり、リクエストボディはJSON形式で格納されています。また、入力フォームから送信された文字列は「URLエンコード」という形式に変換されており、そのままではNode.jsで扱えません。
上記のミドルウェアによって、通常のオブジェクトに変換することが可能です。
解析された値を受けとる
以上でリクエストボディを受け取る準備が整いました。「app.js」に以下の記述を追加し、新たなエンドポイントを作成します。
app.js
app.post('/todo', (req, res) => {
console.log(req.body.title)
})
GET
メソッドの取得にはapp.get
を使用しましたが、POST
メソッドの取得には、app.post
を使用します。
-
Todoのタイトルを追加するルーティングになるため、パスは「
/todo
」とします。 - リクエストボディは
req.body
で受け取ります。今回は「title
」というプロパティが与えられているため、req.body.title
としています。
以上でExpress側の実装は完了しました。
formで情報を渡す
続いて、入力フォームからデータを送信できるようにEJSの記述を変更します。
index.ejs
<body>
<h1>TODO アプリ</h1>
<ul>
<li></li>
</ul>
<div>
<h2>Todo追加</h2>
<form action="/todo" method="post">
<input type="text" name="title" placeholder="todo title" />
<input type="submit" value="送信" />
</form>
</div>
</body>
POST
リクエストの送信には、上記のようにformタグを使用します。name
属性の値をプロパティ名として、フォームの入力データがリクエストボディに格納されます。
ブラウザから「htto://localhost:3000/」に接続し、画面を確認しましょう。前回チャプターで取得した一覧の下に、「Todo追加」の見出しと入力フォームが表示されているはずです。
includeを用いてEJSファイルを分割する
ここで「index.ejs」ファイルを覗いてみると、Todoの一覧と入力フォーム、2つの要素があることに気が付きます。
今回は単純なサンプルコードであるものの、実際のアプリケーション開発では記述量が何倍にも膨れ上がります。将来的なメンテナンス性を考慮すると、あらかじめファイルを分割しておくべきです。
EJS
ではinclude
を使うことで、別のEJSファイルを呼び出すことができました。以下の手順に従ってファイルを分割しましょう。
- 「views」配下に「_share」というディレクトリを作成します。
- 「_share」配下に「todoList.ejs」と「todoForm.ejs」を作成します。
~/views/ (Mac / Linux)
cd views
mkdir _share
touch ./_share/todoList.ejs && touch ./_share/todoForm.ejs
~/views/ (Windows)
cd views
mkdir _share
New-item ./_share/todoList.ejs && New-item ./_share/todoForm.ejs
各ファイルの内容は以下の通りです。
todoList.ejs
<ul>
<li></li>
</ul>
todoForm.ejs
<div>
<h2>Todo追加</h2>
<form action="/todo" method="post">
<input type="text" name="title" placeholder="todo title" />
<input type="submit" value="送信" />
</form>
</div>
index.ejs
<body>
<h1>TODO アプリ</h1>
</body>
コンポーネント志向
このようにアプリケーションを小さな部品に分ける手法は「コンポーネント志向」とも呼ばれ、現代の開発環境を特徴づけるものです。コンポーネント単位の設計を行うことで、保守性や拡張性の高いシステムが構築できます、
ファイルの記述が終わったら、実際にリクエストを送信してみましょう。
サーバーを起動してブラウザを開き、フォームに適当な文字列を入力して「送信」を押しましょう。成功すれば、入力した文字列がサーバーからレスポンスとして返されます。
送信したデータをDBに登録する
入力フォームから正しく文字列を送ることができました。それでは、サーバー側で受け取った文字列をDBに登録しましょう。
「app.js」のファイルを開き、app.post('/todo')
のコードを変更します。
app.js
app.post('/todo', async (req, res, next) => {
// ① title取得
const title = req.body.title
try {
// ② DB選択
await executeQuery(mysqlClient, 'USE express_mysql_todo')
// ③ 取得したtitleをDBに登録
await executeQuery(mysqlClient, 'INSERT INTO todos (title) VALUES (?)', [
title,
])
// ④ 一覧を再度取得
const { results } = await executeQuery(mysqlClient, 'SELECT * FROM todos')
// ⑤ index.ejsを表示
res.render('index.ejs', { todos: results })
} catch (err) {
console.log(err)
}
})
①リクエストボディの取得、②DBとの接続、③データの読み込み、⑤データの表示については既出の内容です。
③取得したデータをDBに登録する処理を確認してください。executeQuery
は以下の働きをしています。
INSERT
によるクエリを発行し、DBにデータを挿入しています。(?)
は動的な値が入ることを意味します。[title,]
は引数として、(?)
に渡される値を指定します。
画面から動作確認を行う
フォームに値を入力し、「送信」を押してみましょう。入力した文字列がリストに追加されていれば問題ありません。
たとえば下記画像では、リストに「新規」が追加されています。
Todo登録
まとめ
今回はTodoアプリに登録機能を追加しました。長いChapterとなったため、最後にポイントをまとめておきます。
- 入力フォームの情報は
POST
メソッドによりリクエストボディに格納される Express
でリクエストボディを受け取るにはexpress.json
とexpress.urlencoded
のミドルウェアが必要- 必要なデータの取得には
app.post
メソッド内でreq.body
を参照する - クエリの動的な値には「?」を使用し、
executeQuery
の第3引数として渡す
次はLessonの総決算として、登録したデータを「更新」します。最後まで振り落とされないように頑張ってください。

Lesson 5
Chapter 5
画面から入力した内容で更新する
Chapter4では、CRUD
の「Create」として、フォームの入力内容をDBに登録しました。今回はCRUD
の「Update」として、Todoリストのタイトルを更新します。
すでにあるデータを書き換えたい場合、今までとは異なるHTTPメソッドを使用します。また、Express側でのルーティングとあわせて、EJS側で画面切り替えの実装をしなければなりません。
Lesson5を通してアプリケーションの基本を学んできましたが、今回はその決算的な内容です。最後まで腰を据えて取り組んでください。
PUT・PATCHメソッド
クライアントからサーバーのデータを更新したい場合、原則としてHTTPメソッドの「PUT」もしくは「PATCH」が使用されます。両者は似た性質を持っており、しばしば混乱を招くため注意が必要です。
実践に入る前に、PUTとPATCHの違いを押さえておおきましょう。端的にまとめると、両者の違いは以下の通りです。
PUTメソッド | PATCHメソッド |
---|---|
既存のリソースを完全に上書きする | 既存のリソースを部分的に上書きする |
具体的なコードを例示しながら、以下で詳しく解説します。
PUTメソッド:データを全部更新する
対象となるデータを丸ごと書き換えたい場合は、PUTメソッドを使用します。
たとえば、人物の姓名と年齢が格納された、以下のデータがあるとしましょう。このうち姓を「tanaka」→「yamada」に更新したい場合を考えてみます。
{
lastName: 'tanaka',
firstName: 'ken'
age: 18
}
クライアントからPUTメソッドを用いて更新します。リクエストの内容は以下の通りです。
{
lastName: 'yamada',
}
リクエストの結果、更新されたデータは下記のようになります。
{
lastName: 'yamada'
}
名前と年齢が削除されてしまいました。送信された値で元のデータが完全に上書きされたためです。このように、PUTメソッドは対象となるデータ全体を更新する特徴を持ちます。
上記の例において、もし姓だけを更新したければ下記のリクエストを送るべきでしょう。
{
lastName: 'yamada',
firstName: 'ken',
age: 18
}
PATCHメソッド:部分更新
対象となるデータを部分的に更新したい場合は、PATCHメソッドを使用します。
先ほどの例で確認しましょう。同じデータに対して、PATCHメソッドで以下のリクエストを送ります。
{
lastName: 'yamada',
}
リクエストの結果として、データ配下の通り更新されるはずです。
{
lastName: 'yamada',
firstName: 'ken'
age: 18
}
今度は名前や年齢の値が削除されていません。このように、指定した値だけを部分的に更新するのが「PATCH」メソッドの特徴です。
PUTとPATCH:どちらを使うべきか
解説した通り、PUTとPATCHには更新範囲の違いがあります。どちらが良いということはありません。データを全面的に差し替えたい場合はPUT、差分のみ更新したい場合はPATCHと、用途に応じて使い分けるべきでしょう。
今回のTodoアプリでは、既存のデータであるtitle
を更新します。データ全体の差分を取る必要はないため、PUTメソッドを採用することにしましょう。
viewの変更
使用するHTTPメソッドが分かったところで、いよいよ実装に移ります。
今回は更新画面を新たに作る必要があるため、どうしても工程を重ねななければなりません。全体としては、以下の3パートに分割されます。
- EJSのパーツ分割
- 更新画面の追加
- 更新完了画面の追加
以下、順を追って解説していきます。
EJSのパーツ分割
まずはEJSのinclude機能を用いて、共通パーツを作成します。異なる画面間で同じパーツを使い回すことで、コードの見通しが良くなる上に、全体の記述量を大幅に減らせます。
既存の「_share」ディレクトリに、以下のファイルを追加してください。
- head.ejs
- pageTitle.ejs
~/express-mysql-app/views/_share (Mac / Linux)
touch head.ejs
touch pageTitle.ejs
~/express-mysql-app/views/_share (Windows)
New-item head.ejs
New-item pageTitle.ejs
作成したファイルを以下の通り編集します。
head.ejs
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
pageTitle.ejs
<h1></h1>
上記をそれぞれ共通ヘッダー、共通ページタイトルとして使い回します。見ての通り小さなパーツですが、それぞれ明確な機能を持っており、分割する単位として適切です。
メイン画面の追加
分割したパーツをそれぞれの画面から呼び出します。新しく追加する画面のファイルは以下の2つです。
- 更新画面:
edit.ejs
- 更新完了画面:
complete.ejs
いずれも「views」配下に作成してください。
ターミナル (Mac / Linux)
cd views
touch edit.ejs
touch complete.ejs
ターミナル (Windows)
cd views
New-item edit.ejs
New-item complete.ejs
それぞれ以下の通り編集していきましょう。
edit.ejs
<!DOCTYPE html>
<html lang="en">
<body>
<form action="/todo/?_method=PUT" method="post">
<input type="text" name="title" placeholder="todo title" value="">
<input type="submit" value="更新">
</form>
</body>
</html>
complete.ejs
<!DOCTYPE html>
<html lang="en">
<body>
<h2>完了</h2>
<a href="/todo">戻る</a>
</body>
</html>
以上で更新画面・更新完了画面のテンプレートが完成しました。各ファイルの説明は以下の通りです。
edit.ejs
選択したTodoのタイトルを編集するページです。
form
タグを配置しています。ここに入力された文字列がPUT
メソッドによって送信され、Todoのタイトルを更新します。form
タグのmethod
がPOSTになっていますが、これは所定の記述です。action
のクエリパラメーターに?_method=PUT
が指定されていることに注目してください。後述しますが、form
タグでExpress
にPUTリクエストを送るときは上記の通り記述します。
complete.ejs
Todoのタイトル更新後に遷移するページです。特筆すべきことはありません。「戻る」を押下するとトップページに戻ります。
編集ページへのリンクを追加
最後に、一覧ページから編集ページへ遷移できるようにリンクを追加します。「todoList.ejs」を開き、以下の通り修正してください。
todoList.ejs
<ul>
<li><a href="todo/"></a></li>
<li></li>
</ul>
以上で'/todo/:id'
へのリンクが設定され、Todoごとの編集ページに遷移できるようになりました。
Expressの変更
viewのファイルは編集できました。あわせてExpress
のルーティングも修正していきましょう。
主な変更としては下記の通りです。
- ミドルウェアに
methodOverride
を追加 - ルーティングパス(GET)を
'/'
=>'/todo'
に変更 - 新規ルーティングパス(GET)として
'/todo/:id'
を追加 - 新規ルーティングパス(PUT)として
'/todo/:id'
を追加
ファイルの内容を確認しましょう。ただコピーするのではなく、必ず変更箇所を確認してください。
app.js
const express = require('express')
const path = require('path')
const methodOverride = require('method-override')
// mysql設定読み込み
const mysqlClient = require('./lib/database/client')
const executeQuery = require('./lib/database/execute')
const app = express()
const PORT = 3000
// ejs用の設定追加
app.set('views', path.join(__dirname, 'views'))
app.set('view engine', 'ejs')
// express 設定
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
app.use(methodOverride('_method'))
app.get('/todo', async (req, res) => {
try {
await executeQuery(mysqlClient, 'USE express_mysql_todo')
const { results } = await executeQuery(mysqlClient, 'SELECT * FROM todos')
res.render('index.ejs', { todos: results })
} catch (err) {
console.log(err)
}
})
app.post('/todo', async (req, res, next) => {
const title = req.body.title
try {
await executeQuery(mysqlClient, 'USE express_mysql_todo')
await executeQuery(mysqlClient, 'INSERT INTO todos (title) VALUES (?)', [
title,
])
const { results } = await executeQuery(mysqlClient, 'SELECT * FROM todos')
res.render('index.ejs', { todos: results })
} catch (err) {
console.log(err)
}
})
app.get('/todo/:id', async (req, res) => {
try {
// mysqlへの接続
await executeQuery(mysqlClient, 'USE express_mysql_todo')
// idから該当のTodo取得
const { results } = await executeQuery(
mysqlClient,
`SELECT * FROM todos WHERE id = ${req.params.id}`
)
// edit.ejsへの遷移、取得したTodoを渡す
res.render('edit.ejs', { todo: results[0] })
} catch (err) {
console.log(err)
}
})
app.put('/todo/:id', async (req, res, next) => {
// idをリクエストパラメーターから取得
const id = req.params.id
// titleをリクエストボディから取得
const title = req.body.title
try {
// mysqlへの接続
await executeQuery(mysqlClient, 'USE express_mysql_todo')
// Todoのtitle更新
await executeQuery(
mysqlClient,
`UPDATE todos SET title = ? WHERE id = ${id}`,
[title, id]
)
// complete.ejsへの遷移
res.render('complete.ejs')
} catch (err) {
console.log(err)
}
})
app.listen(PORT, () => {
console.log(`Starting server on PORT: ${PORT}`)
})
以下は変更点の解説です。
methodOverrideの追加
ミドルウェアとしてmethodOverride
を定義しています。methodOverride
は送信クエリパラメーターで入力されたHTTPメソッドを識別し、PUT
やDELETE
などのHTTPメソッドを識別します。こうすることで、クライアントはPOST要求を通じてPUT要求を行うことが可能です。
method-overrideモジュール
ExpressはGET・POST・PUT・DELETEのHTTPリクエストを送信できますが、ブラウザ側が対応しているのはGET・HOSTだけです。method-overrideを用いることで、ブラウザをPUT・DELETEに対応させることができます。
Topページのルーティングパス変更
ルーティングパスをapp.get('/')
=> app.get('/todo')
に変更しています。意図としてはPUT
の追加でルーティングパスが増えてパスにバラツキが出るからです。
この変更により、トップページにアクセスするときは「http://localhost:3000/todo」を参照する必要があります。
タイトル更新用のパス追加(GET)
Todoのタイトル更新画面として、新たにパスを作成しました。動的なid
というパラメーターを受け取り、Todoの詳細情報を保持したedit.ejs
へ遷移させます。より具体的には、以下の処理を行っています。
mysql
への接続id
に該当するTodoをDBから取得edit.ejs
へ遷移し、取得したTodoを渡す
edit.ejs
タイトル更新用のパス追加(PUT)
実際にTodoのtitle
を更新する箇所です。また、以下の処理を行っています。
- Todoの
id
とtitle
を取得 mysql
への接続- DB上のTodoの
title
を更新 complete.ejs
への遷移
complete.ejs
画面上でTodoの更新を確認する
それでは実際に更新を行ってみましょう。例によってnode app.js
でサーバーを起動し、ブラウザから「localhost:3000/todo」にアクセスします。画像のように、現在のTodo一覧画面が表示されるはずです。
index.ejs
「散歩に行く」をクリックすると更新画面(edit.ejs)へ遷移します。更新フォームへ「散歩に明日行く」と入力します。
edit.ejs
完了画面から「戻る」を押下します。「散歩に行く」から「散歩に明日行く」へ更新されていることが確認できました。一覧画面は現在のTodoをDBから取得します。
DBの値が変更できていることが確認できます。
index.ejs
DBを確認する
念のためDBも確認してみましょう。mysql -u root -p
でパスワードを入力し、MySQLのコンソール画面に入ります。DBに接続後、SELECT
文で確認してください。
MySQL
「散歩に明日行く」と変わっていることが確認できました。データの永続化も問題なく行えています。
まとめ
以上で「DBとの連携」のLessonは終了です。HTTPサーバーとデータベースを連携したことで、画面上に動的なデータを表示できるようになりました。
Chapter5のまとめは以下の通りです。
- データの更新には
PUT
(PATCH
)メソッドを使用する app.put
メソッドでPUT
のルーティングを指定できるUPDATE
文を発行し、対象となるデータを更新する
今回は取り上げませんでしたが、さらに理解を深めたい方はCRUDの「Delete」として、Todoの削除機能を追加してみましょう。データの登録・更新・削除機能を実装すると、一気にWebアプリケーションらしくなります。
どれほど大規模なシステムであっても、個々の部品はCRUD
のような基本理念に従っています。次のLesson6では、エラーハンドリングの基本と実践を学びましょう。
