JavaScriptのWebアプリケーションフレームワーク道
SEとしての1年目も終わりに差し掛かり、個人的にやってきたJavaScriptのWebアプリ作りで学んできたことを整理して、アウトプットするために記事にしました。
今回はExpressからDB(MySQL)への接続についてです。
前回に引き続き、メモ帳アプリを作っていきます。
目標
JavaScriptのWebアプリケーションフレームワークの中で最もシンプルなのが"Express"。
Expressでメモ帳アプリを作成することを目標にします。
記事が長くなりそうだったので、基本編とDB接続編の2本を立てです。
この記事は、DB接続編として前回までに作った部分にCRUD処理を追加していきます。
前回の基本編→SE1年目のJavaScript Webアプリケーションフレームワーク道...Expressで簡易メモ帳アプリ(1)
基本編とDB接続編をまとめた完成版コードはGitHubに置いてありますので、ご自由に御覧ください♪ → sasaken555/expMemo
##やること
- Webアプリケーションフレームワークの利用
- DB(今回はMySQL)との通信を含むCRUD処理
##やらないこと
-
ES6の記法→ SQLを書く中でバッククオートだけ使ってます。 - ORM (O/R マッパー) の利用
- NoSQLDB(MongoDBとか)の利用
実装する要件
基本的なCRUD処理ができれば良いなと。具体的には以下のような感じです。
- メモの一覧表示
- 最初に表示
- 各メモは作成・更新時間も分かるようにする
- 各メモは詳細も見れるものとする
- メモの登録
- タイトルと本文のみ入力する
- メモの更新
- これもタイトルと本文のみ入力する
- メモの削除
- 消す前の確認画面を入れる
#実行環境
動作確認はWindows10、GoogleChrome から実施しています。
以下、メインで使う環境たちです。
名称 | バージョン | 内容 |
---|---|---|
Node.js | 7.4.0 | JavaScriptのランタイム |
NPM | 3.10.9 | パッケージを管理する |
Express | 4.14.0 | Webアプリケーションフレームワーク |
MySQL | 5.7 | リレーショナルデータベース |
前提
Node.js と MySQLはローカルにインストールしています。
#実装
NodejsとMySQLの連携
こちらの記事を参考にさせていただきました。
→Node.jsでMySQLを使うメモ
NodejsとMySQLを連携させるには、node-mysqlを使います。
参考記事と、node-mysqlの公式のREADMEを読みつつ進めます。
Express側の準備
まずはnode-mysqlを手に入れるところから。
公式の通り、ターミナルから以下1行を叩けばOK!
npm install --save mysql
これで後はNodejs側からrequire
で読み込めばMySQLと接続する処理を行えます。
DBからメモ全件のデータを主として、メモ一覧に表示する処理を書いてみます。
var express = require('express');
var router = express.Router();
// DB接続定義
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'EditUser',
password: 'EditYourPass',
database: 'memopad',
port: 3306,
dateStrings: true,
timezone: 'jst'
});
/* 一覧画面表示処理 */
router.get('/', function(req, res, next) {
// メモ一覧をDBから取り出してmemoItemsに格納
var memoItems = [];
// DB接続処理
connection.connect();
var sql = 'SELECT * FROM memo ORDER BY id ASC;'
connection.query(sql, function(error, results, fields) {
if (error) throw error;
memoItems = results;
res.render('index', { pageTitle: 'メモ一覧', memoItems: memoItems });
});
connection.end();
});
createConnection
の部分でMySQLとの接続の準備を行い、
connect()
で接続、end()
で切断します。
これでOK!
DB側の準備
連携先のデータベースとテーブルを以下のような形で準備します。
データベース名:memopad
テーブル名:memo
項目名 | データ型 | Null | Key | その他 |
---|---|---|---|---|
id | int | NO | PRI | AUTO_INCREMENT |
title | varchar(50) | NO | ||
memo | varchar(280) | YES | ||
created | datetime | NO | ||
modified | datetime | YES |
Node.js側ではメモのidを基準に処理を行うので、NOT NULL, PRIMARY KEYに設定しておきます。
DBの準備ができたら、後は5件ほど適当なデータを入れておきます。
一覧の稼働確認
一旦ここまでで動くか確認します。
上手くいけました!
ただし、ここで1つ罠がありました。
Connectionの罠
ここで何度かリロードしたり、一度閉じてもう一度アクセスすると。
接続拒否に。。。
コンソール上には以下のようなエラーが吐かれていました。
Error: Cannot enqueue Handshake after already enqueuing a Handshake.
要は『Connectionが作られているから、2度Connectionを作ってるんじゃねえ』ってことみたいですね。じゃあ『end()を書かなければ1つのConnectionなんでは?』と思い、connection.end();
を削除してみると...
Error: Cannot enqueue Query after invoking quit.
次は『接続切ったのに、接続試みるのやめてもらえます?』と怒られてしまいました。どっちをしてほしいんでしょうか...
接続切らずに、2度のconnectionを作成しないようにするには、以下の記事に同様の現象の対処が載っていました。→Node+Express+MySQL備忘録
結論から言えば、connection.connect()
connection.end()
をどちらも書かないが正解みたいです。もう一度公式を見たら、connect()しなくてもquery()で接続を確立する旨が書いてありましたね。
Establishing connections
However, a connection can also be implicitly established by invoking a query:
なので書き直すとしたらこうなるでしょうか。
/* 一覧画面表示処理 */
router.get('/', function(req, res, next) {
// メモ一覧をDBから取り出してsampleMemosに格納
var memoItems = [];
// DB接続処理
// connection(), end()はそれぞれエラーが出るため記述しない
// query()で接続している
var sql = 'SELECT * FROM memo ORDER BY id ASC;'
connection.query(sql, function(error, results, fields) {
if (error) throw error;
memoItems = results;
res.render('index', { pageTitle: 'メモ一覧', memoItems: memoItems });
});
});
これでエラーが出なくなりました(*'ω'*)
CRUD書き直し
一覧がうまく表示されたところで、他の処理も書いてしまいます。
基本は一覧処理と同じですが、気を付けるべきは時間の処理でした。
/* 詳細画面表示処理 */
// 正規表現(\\d)で /new とルーティングを区別している
router.get('/:memoId(\\d+)', function(req, res, next) {
var memoId = req.params.memoId;
var targetMemo = [];
var sql = `SELECT * FROM memo WHERE id = ${memoId};`
console.log(sql);
connection.query(sql, function(error, results, fields) {
if (error) throw error;
targetMemo = results[0];
res.render('memoDetail', { pageTitle: 'メモ詳細', targetMemo: targetMemo });
});
});
var express = require('express');
var router = express.Router();
// DB接続定義
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'EditUser',
password: 'EditYourPass',
database: 'memopad',
port: 3306,
dateStrings: true,
timezone: 'jst'
});
/* 削除確認画面表示処理 */
router.get('/:memoId', function(req, res, next) {
var memoId = req.params.memoId;
var targetMemo = [];
var sql = `SELECT * FROM memo WHERE id = ${memoId};`
console.log(sql);
connection.query(sql, function(error, results, fields) {
if (error) throw error;
targetMemo = results[0];
res.render('deleteMemo', { pageTitle: 'メモ削除', targetMemo: targetMemo });
});
});
/* 削除処理 */
router.post('/:memoId', function(req, res, next) {
var memoId = req.params.memoId;
// SQLの作成
var sql = `DELETE FROM memo WHERE id = ${memoId}`;
// 削除実行
connection.query(sql, function(error, results, fields) {
if (error) throw error;
res.redirect('/');
});
});
module.exports = router;
詳細表示と削除処理は、表示する部分がほぼ共通しているので書くのは困りませんね。
SQLを作る際にES6のバッククオート記法を使っています。DELETE FROM memo WHERE id = ${memoId}
のところです。こうやって${}
で変数を直接埋め込めるので便利ですね。
ES6については、かなりNode.jsが対応してきているみたいなので、別の機会にフルES6のNode.jsアプリなんか書いてみたいですね。→Node.jsのES6対応状況
続いて新規作成と更新処理を書きます。
時間の処理だけ気を付けていきます。
var express = require('express');
var router = express.Router();
// DB接続定義
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'EditUser',
password: 'EditYourPass',
database: 'memopad',
port: 3306,
dateStrings: true,
timezone: 'jst'
});
/*
日本時間返却処理
mysqlJpTime(date)
date: Date型のUTC時間の値
*/
function mysqlJpTime() {
// UTC時間
var date = new Date();
// UTC時間を日本時間に修正
date.setTime(date.getTime() + 32400000);
// JSのDate型をMySQLのDateTime型に変換
var mysqlJpTime = date.toISOString().slice(0, 19).replace('T', ' ');
return mysqlJpTime;
}
/* 新規登録画面表示処理 */
router.get('/', function(req, res, next) {
res.render('newMemo', { pageTitle: '新規メモ作成' });
});
/* 新規登録処理 */
router.post('/', function(req, res, next) {
var title = req.body.title;
var memo = req.body.memo;
var createdTime = mysqlJpTime();
var post = {title: title, memo: memo, created: createdTime};
var sql = `INSERT INTO memo SET ?`
connection.query(sql, post, function(error, results, fields) {
if (error) throw error;
res.redirect('/');
});
});
module.exports = router;
var express = require('express');
var router = express.Router();
// DB接続定義
var mysql = require('mysql');
var connection = mysql.createConnection({
host: 'localhost',
user: 'EditUser',
password: 'EditYourPass',
database: 'memopad',
port: 3306,
dateStrings: true,
timezone: 'jst'
});
/*
日本時間返却処理
mysqlJpTime(date)
date: Date型のUTC時間の値
*/
function mysqlJpTime() {
// UTC時間
var date = new Date();
// UTC時間を日本時間に修正
date.setTime(date.getTime() + 32400000);
// JSのDate型をMySQLのDateTime型に変換
var mysqlJpTime = date.toISOString().slice(0, 19).replace('T', ' ');
return mysqlJpTime;
}
/* 更新画面表示処理 */
router.get('/:memoId', function(req, res, next) {
var memoId = req.params.memoId;
var targetMemo = [];
var sql = `SELECT * FROM memo WHERE id = ${memoId};`
connection.query(sql, function(error, results, fields) {
if (error) throw error;
targetMemo = results[0];
res.render('editMemo', { pageTitle: 'メモ更新', targetMemo: targetMemo });
});
});
/* 更新処理 */
router.post('/:memoId', function(req, res, next) {
// 更新する値
var title = req.body.title;
var memo = req.body.memo;
var modifiedTime = mysqlJpTime();
var memoId = req.params.memoId;
// SQLの作成
var sql = 'UPDATE memo SET title = ?, memo = ?, modified = ? WHERE id = ?';
var post = [title, memo, modifiedTime, memoId];
sql = mysql.format(sql, post);
// 更新実行
connection.query(sql, function(error, results, fields) {
if (error) throw error;
res.redirect('/');
});
});
module.exports = router;
ここで引っかかったのは、JavaScriptの時間の処理。以下2つの問題への対処が必要です。ややこしい処理はひとまとめにして目的の時間を返すmysqlJpTime(date)
を定義しています。
- new DateはUTC時間
new Date
で現在時刻を取得すると、UTC時間になっているようですね。
日本時間に直すためには、9時間分遅らせなきゃいけません。
右の記事を参考にさせていただいて、以下のように書きました。
→node.jsで現在の時刻を自動で取得する
date.setTime(date.getTime() + 32400000);
- MySQLのdatetimeにJavaScriptのDateを合わせる
また、MySQLをdatetime型でテーブル定義してしまいましたので、これまた変換しなければいけません。下の参考記事の中で最もシンプルなものを採用です。
var mysqlJpTime = date.toISOString().slice(0, 19).replace('T', ' ');
これですべての実装が完了!
稼働確認
いつも通り、npm start
からの localhost:3000
で一覧から確認。
何とか全ての処理ができたようです(*'ω'*)
#まとめ
2回に渡ってやってきたExpressを使ったメモ帳アプリの作成から、体感は以下の感じです。
- ExpressはWebアプリ作成の骨となるルーティングや画面表示までは容易にできる。
- しかも強力なコード自動生成で雛形を作ってくれるので、必要な部分に手を加えればOK
- DB等の別の機能との連携まで手を伸ばすと、別途Express外の機能の理解が必要。
- 裏を返せば、他の機能との連携に制約がなく、自由にカスタマイズ可能とも言えそう。
今回はExpressを扱ってみましたが、他のWebアプリケーションフレームワークも触って比較してみたいと思います。それはまた別の機会に。
以上