Express.js(node.js)からMySQLへの接続とCRUD操作
express.js(node.js)を利用してアプリケーションを構築しようとした場合に最初に思いつく疑問の一つがどうやってデータベースの操作を行うのだろうか?といったものではないでしょうか。本文書では実際のコードを利用したシンプルな例を通してexpress.jsからMySQLを操作する下記の6つのSQL文の使用方法について説明を行なっています。本書を読み終えるとnode.jsからMySQLデータベースに対してCRUD(Create, Read, Update, Delete)の操作を理解することができます。
- create database
- creata table
- select
- insert
- delete
- update
動作確認はMAC環境で行なっておりnode.jsを事前にインストールしておく必要があります。
目次
MySQLのインストール
Homebrewを利用してMySQLのインストールを行います。MAC環境へのMySQLへのインストール方法については、下記の文書の前半を参考に行なってください。
注意点:MYSQLバージョン8の場合
MySQLのバージョンが8の場合はnode.jsからMysqlへの接続の際にError: ER_NOT_SUPPORTED_AUTH_MODE: Client does not support authentication protocol requested by server;といったエラーが表示されます。この場合はMysqlに接続し、下記の設定を行うことで回避することが可能です。
$ mysql -u root
mysql> alter user root@localhost identified with mysql_native_password by '';
Query OK, 0 rows affected (0.01 sec)
MySQLの5.7では上記の設定は必要ありません。
Express.jsのインストール
express.jsをインストールするディレクトリを任意の名前で作成します。ここではexpress_mysqlとしています。
$ mkdir express_mysql
$ cd express_mysql/
express.jsで使用するパッケージ管理を行うためnpm initコマンドでpackage.jsonファイルの作成を行います。-yオプションをついて対話モードをスキップします。
$ npm init -y
npmを実行したディレクトリの中にpackage.jsonファイルが作成されます。
次にexpress.jsのインストールを行います。
$ npm install express
Express.jsの動作確認
Hello Worldの表示
インストールが完了したら、Express.jsの動作確認を行うためにアクセスしたブラウザにHello Worldを表示させます。
express_mysqlディレクトリの中にapp.jsファイルを作成して、下記を記述してください。
const express = require('express')
const app = express()
const port = 3000
app.get('/', (req, res) => res.send('Hello World!'))
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
nodeコマンドを使って、作成したapp.jsファイルを実行します。
$ node app.js
Example app listening on port 3000!
ブラウザを起動し、localhostのポート3000にアクセスして下記の画面が表示されたらExpress.jsは正常に動作しています。
nodemonのインストール
node app.jsコマンドを実行してexpress.jsの動作確認を行いましたが、nodeコマンドを使った場合はファイルの変更を行う度にコマンドを再実行する必要があります。ファイルの変更の度にnodeコマンドを実行するのは効率的ではないためファイルの更新を監視して自動でnodeコマンドを実行してくれるnodemonをインストールします。
$ npm install nodemon
インストール完了後、npxコマンドでnodemonを実行してください。今後はapp.jsファイルの更新を行うと自動でnode app.jsが再実行されます。
$ npx nodemon app.js
[nodemon] 1.19.2
[nodemon] to restart at any time, enter `rs`
[nodemon] watching dir(s): *.*
[nodemon] starting `node app.js`
Example app listening on port 3000!
MySQLとの動作確認
MySQL接続ドライバのインストール
express.jsからMySQLへ接続するためには、MySQLへ接続するためのドライバをインストールする必要があります。
$ npm install mysql
express.jsからMySQLへの接続
app.jsからMySQLへの接続確認を行います。app.jsファイルのport行の下にmysqlへの接続に関するコードを追加します。MySQLに接続するためには、host, user, passwordの情報が必要になります。passwordが”(ブランク)になっているのはパスワードが設定されていないためです。インストールしたMySQLのパスワードを設定している場合はここにパスワードを入れてください。
const port = 3000
const mysql = require('mysql');
const con = mysql.createConnection({
host: 'localhost',
user: 'root',
password: ''
});
con.connect(function(err) {
if (err) throw err;
console.log('Connected');
});
追加後、ファイルを保存し、接続が正常に行われるとnodemonコマンドを実行した端末上にConnected!のログが表示されます。
[nodemon] restarting due to changes...
[nodemon] starting `node app.js`
Example app listening on port 3000!
Connected
MySQLデータベースの作成と接続
MySQLにデータベースの作成を行なっていないので、接続後にデータベースの作成を行います。データベースの作成はCREATE DATABASEコマンドで行います。Express.jsではSQL文を実行するためにqueryメソッドを利用します。
con.connect(function(err) {
if (err) throw err;
console.log('Connected');
con.query('CREATE DATABASE express_db', function (err, result) {
if (err) throw err;
console.log('database created');
});
});
実行するとdatabase Createdというログが表示されます。データベースは1つしか作成できないので作成後はデータベース作成のコードは削除してください。
データベースの作成が完了したので接続オプションに作成したデータベース情報を追加して、createConnectionメソッドでデータベースへの接続まで行います。
const con = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'express_db' //追加
});
テーブルの作成
データベースへの接続が完了したのでテーブルの作成を行います。テーブルの作成はCREATE TABLE文で行います。
作成するテーブルの名前はusersでid, name, emailの3つの列を持ち、idはオートインクリメントで自動で1ずつ増える整数でプライマリキーに設定しています。残りの2つのname, emailはVARCHARでNOT NULLを設定します。
SQL文だけ取り出すと以下の通りです。
CREATE TABLE users (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL)
CREATE TABLE文を追加したコードは以下の通りです。
con.connect(function(err) {
if (err) throw err;
console.log('Connected');
const sql = 'CREATE TABLE users (id INT NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL)';
con.query(sql, function (err, result) {
if (err) throw err;
console.log('table created');
});
});
変更を保存するとnodemonにより実行され、テーブルが作成されます。
作成したテーブルはデータベース管理GUIのTablePlus等を使って確認することができます。
TablePlusで確認すると作成したusersテーブルを確認することができます。
select文の動作確認
select文によりデータの取得
データベースからselect文によりデータ取得の動作確認を行うため、TAblePlusを利用して直接データを挿入します。
登録後、usersテーブルの1件しかありませんがselect文を使って全データを取得するselect * from usersを実行します。
con.connect(function(err) {
if (err) throw err;
console.log('Connected');
const sql = "select * from users"
con.query(sql, function (err, result, fields) {
if (err) throw err;
console.log(result)
});
});
実行後、先ほど登録したusersのデータが下記のように取得できます。
[ RowDataPacket { id: 1, name: 'John', email: 'John@example.com' } ]
emailだけ取り出したい場合は、下記のように記述することで取得したデータからemailのみ取り出すことができます。
console.log(result[0].email)
テーブル取得データをブラウザで表示
次はselect文を使用して取得したデータをアクセスしたブラウザに返します。
const con = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'express_db'
});
con.connect(function(err) {
if (err) throw err;
console.log('Connected');
});
app.get('/', (request, response) => {
const sql = "select * from users"
con.query(sql, function (err, result, fields) {
if (err) throw err;
response.send(result)
});
});
ブラウザからlocalhost:3000にアクセスするとテーブルのデータ表示されます。
insert文の動作確認
insert文によるデータの挿入
TablePlusを使って先ほどは1件ユーザデータを登録しましたが、今回はinsert文を利用してデータの挿入を行います。
実行するINSERT文は下記の通りです。
INSERT INTO users(name,email) VALUES('kevin','kevin@test.com')
実行するコードは下記の通りです。
const sql = "INSERT INTO users(name,email) VALUES('kevin','kevin@test.com')"
con.query(sql,function(err, result, fields){
if (err) throw err;
console.log(result)
})
console.logでresultに入っている情報を確認するとaffectedRow(追加した行)などがOKPacketオブジェクトに入って戻されることがわかります。
OkPacket {
fieldCount: 0,
affectedRows: 1,
insertId: 2,
serverStatus: 2,
warningCount: 0,
message: '',
protocol41: true,
changedRows: 0
}
TablePlusで確認するとinsertしたデータが追加していることを確認できます。
またブラウザでlocalhost:3000にアクセスすると取得できるデータが2件になっていることも確認できます。
insert文で直接Valuesの中に値を入れていましたが、?を使うこともできます。
const sql = "INSERT INTO users(name,email) VALUES(?,?)"
con.query(sql,['Jack','jack@exsample.co.jp'],function(err, result, fields){
if (err) throw err;
console.log(result)
})
下記のようにSET ?を使うこともできます。
const sql = "INSERT INTO users SET ?"
con.query(sql,{name:'Tommy',email:'tommy@test.co.jp'},function(err, result, fields){
if (err) throw err;
console.log(result)
});
どちらも同じようにテーブルへのデータの追加を行うことができます。
入力フォームの作成
ブラウザから入力したデータをデータベースのテーブルに追加(insert)する方法を確認します。ブラウザ側で入力が行えるようにするためには、フォームを含んだHTMLをブラウザに渡す必要があります。
フォーム用のhtmlファイルを保存するhtmlディレクトリを作成し、その中にform.htmlファイルを作成します。中身はnameとemailのinput要素を2つ含んだシンプルな入力フォームです。CSS等のスタイル設定は全く行なっていません。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>入力フォーム</title>
</head>
<body>
<h1>入力フォーム</h1>
<form action="/" method="POST">
name <input type="text" name="name"><br>
email <input type="text" name="email"><br>
<button type="submit">送信</button>
</form>
</body>
</html>
入力フォームが表示されるようにapp.jsの”/(ルート)”のルーティングを変更します。sendFileメソッドを使用して、html/form.htmlファイルを”/(ルート)”にアクセスしてきたブラウザに渡しています。
app.get('/', (req, res) =>
res.sendFile(path.join(__dirname, 'html/form.html')))
ブラウザで確認するとnameとemailの入力欄があるフォームが表示されます。
body-parserのインストール
ブラウザで入力したnameとemailはPOSTリクエストによってexpress.js側に送信されます。送信されたnameとemailをexpress.jsで処理するためには、body-parserモジュールをインストールする必要があります。
$ npm install body-parser
インストールが完了したら、body-parserを読み込みます。
const path = require('path')
const express = require('express')
const bodyParser = require('body-parser')
const app = express()
const port = 3000
読み込んだbody-parserをミドルウェアとして設定します。
app.use(bodyParser.urlencoded({ extended: true }));
入力フォームからPOSTリクエストで送信されるデータを取得できるか確認を行うため追加のルーティングを設定します。req.bodyの中に送られてきたデータが保存されています。
app.get('/', (req, res) =>
res.sendFile(path.join(__dirname, 'html/form.html')))
app.post('/', (req, res) => res.send(req.body)) //追加
入力フォームに情報を入力した送信ボタンを押します。
express.js側ではPOSTリクエストから送られてきた内容を受け取り、res.sendでreq.bodyをブラウザに送り返しているので、ブラウザには以下のようにreq.bodyの中の値が表示されます。中身は入力フォームで入力した
フォームから送られてきたデータをinsert
入力フォームの値を先ほど確認したinsert文を使ってMySQLデータベースのテーブルに追加します。追加が正常に完了するとブラウザの画面には、”登録が完了しました”と表示されます。
app.post('/', (req, res) => {
const sql = "INSERT INTO users SET ?"
con.query(sql,req.body,function(err, result, fields){
if (err) throw err;
console.log(result);
res.send('登録が完了しました');
});
});
実際に入力フォームを入れて、送信ボタンを押すと下記の画面が表示されます。TablePlus等を使ってデータが追加されているか確認を行なってください。
テンプレートエンジンejsの設定
delete文やupdate文を行うためにhtml文の中で変数を使用したいのでHTMLとJavaScriptを一緒に記述することができるテンプレートエンジンejsを利用します。
ejsのインストール
npmコマンドを使用してejsモジュールのインストールを行います。
$ npm install ejs
ejsの動作確認
ejsを利用して、テーブルから取得したユーザ情報の一覧を表示してみましょう。
ejsの利用するためにはrequire文を追加します。
const path = require('path')
const express = require('express')
const bodyParser = require('body-parser')
const ejs = require('ejs') //追加
const app = express()
const port = 3000
テンプレートエンジンにejsを利用するために下記の設定が必要となります。
app.set('view engine', 'ejs');
下記のように”/(ルート)”へのアクセスが行われるとselect文が実行され、実行結果をejsファイルに渡します。そのためにrenderメソッドを使って、indexという名前のejsファイルを指定しています。
下記ではindexと指定しますが、viewsディレクトリの中のindex.ejsファイルを指定していることになるため、viewsディレクトリを作成し、その中にindex.ejsファイルを保存します。{users : result}でusersという名前の変数にselect文の実行結果resultを渡しています。index.ejsファイルではusers変数を使用してselect文の実行結果を表示することができます。
app.get('/', (req, res) => {
const sql = "select * from users";
con.query(sql, function (err, result, fields) {
if (err) throw err;
res.render('index',{users : result});
});
});
index.ejsファイルは下記のように記述します。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>User list</title>
</head>
<body>
<table>
<tr>
<th>名前</th>
<th>email</th>
<th>更新</th>
<th>削除</th>
</tr>
<% users.forEach(function (value) { %>
<tr>
<td><%= value.name %></td>
<td><%= value.email %></td>
<td><a href="/edit/<%= value.id %>">更新</a></td>
<td><a href="/delete/<%= value.id %>">削除</a></td>
</tr>
<% }); %>
</table>
</body>
</html>
ブラウザから”/(ルート)”にアクセスするとこれまでにテーブルに追加したユーザ情報の一覧が下記のように表示されます。
delete文の動作確認
delete文を使って、ユーザ一覧にあるユーザ情報を削除する方法を確認します。
delete文によりデータ削除
実行するDELETE文は下記の通りです。?にはユーザデータのidを指定します。
const sql = "DELETE FROM users WHERE id = ?";
実行するコードは下記の通りです。ルーティングに/delete/:idを追加します。:idに入ってくる値は、ブラウザから送られてくるreq.params.idで取得することができます。WHERE句でidの値にreq.params.idを設定し、そのidを持つ行を削除する処理を行なっています。
app.get('/delete/:id',(req,res)=>{
const sql = "DELETE FROM users WHERE id = ?";
con.query(sql,[req.params.id],function(err,result,fields){
if (err) throw err;
console.log(result)
res.redirect('/');
})
});
ユーザ一覧に表示されるユーザ情報を削除するためには、各行の右側に表示されている削除リンクをクリックします。
削除リンクをクリックするとどのような処理が行われているか確認するためにユーザ一覧を表示させているindex.ejsの削除部分のコードを確認します。/delete/の後には、value.idでその行のidを取り出して設定しています。
<td><a href="/delete/<%= value.id %>">削除</a></td>
削除リンクには、/delete/id(行のidが設定)にリンクが貼られています。
上から6番目のTedの行にある削除リンクをクリックすると削除が行われ、”/(ルート)”にリダイレクトされ、Ted行が削除されたユーザ一覧が表示されます。
update文による動作確認
update文を使って、ユーザ一覧にあるユーザ情報を更新する方法を確認します。
updateによる更新
実行するUPDATE文は下記の通りです。SETの?にはブラウザから送られているPOSTリクエストの内容を設定します。
const sql = "UPDATE users SET ? WHERE id = " + req.params.id;
実行するコードは下記の通りです。ルーティングに/update/:idを追加しています。deleteと同様に:idに入る値は、req.params.idで取得することができます。WHERE句でidの値にreq.params.idを設定しそのidを持つ行を更新する処理を行なっています。更新内容はinsert文で使用したのと同じようにreq.bodyを使っています。
app.post('/update/:id',(req,res)=>{
const sql = "UPDATE users SET ? WHERE id = " + req.params.id;
con.query(sql,req.body,function (err, result, fields) {
if (err) throw err;
console.log(result);
res.redirect('/');
});
});
更新するためのルーティングは追加できましたが、現在の値を更新するためには新しい値を入力するための更新フォームが必要となります。
更新フォームの作成
データを更新したい場合は、更新処理のためのルーティングを追加するだけではなく更新のデータを入力するフォームが必要となります。そのため更新画面を表示させるルーティングを追加します。
更新画面へのルーティング/edit/:idを追加します。select文とidを利用して更新したい行の現在の値を取得します。ブラウザに表示させる画面は取得した値を渡すためことができるejsファイルを使って作成します。ejsファイルには変数userを使って取得した値を渡します。
app.get('/edit/:id',(req,res)=>{
const sql = "SELECT * FROM users WHERE id = ?";
con.query(sql,[req.params.id],function (err, result, fields) {
if (err) throw err;
res.render('edit',{user : result});
});
});
viewsディレクトリの中にedit.ejsファイルを作成します。edit.ejsではselect文で取得したデータを持つ変数userの値を利用します。input要素のvalueにname, emailの現在の値であるuser[0].name, user[0].emailをそれぞれ設定します。POSTリクエストは先ほど追加した更新処理を行うルーティング先である/update/:idに行われます。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>User Edit</title>
</head>
<body>
<h1>更新フォーム</h1>
<form action="/update/<%= user[0].id %>" method="POST">
name <input type="text" name="name" value="<%= user[0].name %>"><br>
email <input type="text" name="email" value="<%= user[0].email %>"><br>
<button type="submit">更新</button>
</form>
</body>
</html>
更新画面へのURLはindex.ejsの更新のaタグのリンクに設定されています。
<td><a href="/edit/<%= value.id %>">更新</a></td>
下記のユーザ一覧の中の名前Jimmyの右側にある更新リンクをクリックします。
更新フォーム画面が表示され、入力フォームの中には現在の値が設定されています。
nameをJimmyからJohnsonに変更します。入力が完了したら、更新ボタンをクリックします。
先ほどまでJimmyだった行の名前がJohnsonに変更されていることがわかります。更新は完了です。
更新処理は完了です。
まとめ
express.jsからMySQLデータベースへの接続だけではなく、データベースの作成、テーブルの作成、データの追加/更新/削除など基本操作を理解することができたと思うので、次は実践的なアプリケーションの構築にチャレンジしてください。
今回作成したapp.jsファイルは下記の通りです。
const path = require('path')
const express = require('express')
const bodyParser = require('body-parser')
const ejs = require('ejs')
const app = express()
const port = 3000
app.use(bodyParser.urlencoded({ extended: true }));
app.set('view engine', 'ejs');
const mysql = require('mysql');
const con = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'express_db'
});
// con.connect(function(err) {
// if (err) throw err;
// console.log('Connected');
// });
app.get('/', (req, res) => {
const sql = "select * from users";
con.query(sql, function (err, result, fields) {
if (err) throw err;
res.render('index',{users : result});
});
});
app.post('/', (req, res) => {
const sql = "INSERT INTO users SET ?"
con.query(sql,req.body,function(err, result, fields){
if (err) throw err;
console.log(result);
res.redirect('/');
});
});
app.get('/create', (req, res) =>
res.sendFile(path.join(__dirname, 'html/form.html')))
app.get('/edit/:id',(req,res)=>{
const sql = "SELECT * FROM users WHERE id = ?";
con.query(sql,[req.params.id],function (err, result, fields) {
if (err) throw err;
res.render('edit',{user : result});
});
});
app.post('/update/:id',(req,res)=>{
const sql = "UPDATE users SET ? WHERE id = " + req.params.id;
con.query(sql,req.body,function (err, result, fields) {
if (err) throw err;
console.log(result);
res.redirect('/');
});
});
app.get('/delete/:id',(req,res)=>{
const sql = "DELETE FROM users WHERE id = ?";
con.query(sql,[req.params.id],function(err,result,fields){
if (err) throw err;
console.log(result)
res.redirect('/');
})
});
app.listen(port, () => console.log(`Example app listening on port ${port}!`));
入力フォームを表示されるルーティングを”/(ルート)”から/createに変更しています。
ユーザ情報一覧のindex.ejsは下記となります。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>User list</title>
</head>
<body>
<p><a href="/create">ユーザ追加</a></p>
<table>
<tr>
<th>名前</th>
<th>email</th>
<th>更新</th>
<th>削除</th>
</tr>
<% users.forEach(function (value) { %>
<tr>
<td><%= value.name %></td>
<td><%= value.email %></td>
<td><a href="/edit/<%= value.id %>">更新</a></td>
<td><a href="/delete/<%= value.id %>">削除</a></td>
</tr>
<% }); %>
</table>
</body>
</html>
index.ejsは上部に入力フォームへのリンクを追加しています。
入力フォームのform.htmlは下記の通りです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>入力フォーム</title>
</head>
<body>
<h1>入力フォーム</h1>
<form action="/" method="POST">
name <input type="text" name="name"><br>
email <input type="text" name="email"><br>
<button type="submit">送信</button>
</form>
</body>
</html>
更新フォームのedit.ejsファイルは下記の通りです。
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>User Edit</title>
</head>
<body>
<h1>更新フォーム</h1>
<form action="/update/<%= user[0].id %>" method="POST">
name <input type="text" name="name" value="<%= user[0].name %>"><br>
email <input type="text" name="email" value="<%= user[0].email %>"><br>
<button type="submit">更新</button>
</form>
</body>
</html>