はじめに
Dcokerの使い方を学んだので、Docker上でNode.jsアプリケーションを作ってみました。
非常に多くの記事やサイトを参考にして作ったので、自分用のまとめ的な側面もあります。
作成したのは簡単なToDoリストアプリです。
初心者なので色々おかしな点があると思いますが、気づいたらコメントで教えていただけるとありがたいです。
完成イメージ
CRUD機能を搭載した簡単なToDoリストを作ります。
完成イメージは以下の通りです。
一番上のテキストボックスにタスクを入力し、右にある「add!」ボタンを押すことでタスクを追加できます。(Create)
その下には、既にデータベースに保存されているタスクが表示されます。(Read)
これらのタスクは、チェックボックスやテキストボックスの値を書き換えることで、自動的に更新されます。(Update)
また、タスク右の「delete」ボタンを押すことで、タスクを削除できます。(Delete)
※テキストボックスの値は、Enterキーを押すかフォーカスが外れたときに入力が確定します。
主な参考サイト
基本的には以下のサイトを参考にしていますが、一部自分なりにアレンジしています。
- DockerでNode.jsアプリケーションを開発する (1) Express.jsをコンテナ内で動かす - Ishida-IT LLC
- DockerでNode.jsアプリケーションを開発する (3) MySQL用コンテナを追加 - Ishida-IT LLC
- [Vue.js + Express + Sequelize + DockerでCRUD搭載のTodoリストを作ってみる - Qiita]
(https://qiita.com/yoshiplum/items/129e7ad1ffc3a02b9eb2)
使用したソフトウェア等
DockerホストのPCはWindows10を使用しました。WSL2でUbuntuを実行し、Ubuntu上で作業を行いました。
- Windows10 バージョン 2004
- Ubuntu 20.04.2 LTS (Focal Fossa)
- Docker 20.10.2
- Node.js 14.16.0
- express 4.16.1
- sequelize 6.5.0
- sequelize-cli 6.2.0
- VueCLI 4.5.0
- Vue.js 3.0.0
- MySQL 8.0.23
システムの全体構成
システムは、3つのコンテナで構成されています。
-
db
コンテナ(MySQLイメージを使用)- ToDoリストアプリのデータを保管するデータベースサーバーです。
-
vue
コンテナ(Node.jsイメージを使用)- ユーザーからのリクエストを受けるサーバーです。今回はフロントエンドにVue.jsを使うため、Vue CLIをインストールします。
-
api
コンテナ(Node.jsイメージを使用)- MySQLサーバーを操作するAPIを提供するサーバーです。
vue
コンテナは本サーバーが提供するAPIを利用してデータベースを操作します。Sequelizeをインストールします。
- MySQLサーバーを操作するAPIを提供するサーバーです。
#プロジェクトのディレクトリを作成
ディレクトリの構成は以下のようになります。
- todo_app
- docker-compose.yml
- .env
- api
- (expressの雛型ファイル群が入る)
- vue
- (Vueの雛型ファイル群が入る)
- db
- logs
- mysql-error.log
- mysql-query.log
- mysql-slow.log
- conf
- my.cnf
- logs
以下のコマンドを適当なディレクトリで実行してください。
mkdir -p todo_app/api
mkdir -p todo_app/vue
mkdir -p todo_app/db/logs
mkdir -p todo_app/db/conf
touch todo_app/docker-compose.yml
touch todo_app/.env
touch todo_app/db/logs/mysql-error.log
touch todo_app/db/logs/mysql-query.log
touch todo_app/db/logs/mysql-slow.log
touch todo_app/db/conf/my.cnf
docker-compose.ymlファイルを編集
todo_app/docker-compose.yml
ファイルを以下のように編集します。
version: '3'
services:
db:
# 起動するイメージを指定
image: mysql:8.0.23
# 環境変数を設定
environment:
- MYSQL_ROOT_HOST=${DB_ROOT_HOST}
- MYSQL_DATABASE=${DB_NAME}
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASS}
- MYSQL_ROOT_PASSWORD=${DB_PASS}
- TZ=${TZ}
# ホスト側のポート:コンテナのポート
ports:
- '3306:3306'
# ボリュームバインド
volumes:
- ./db/conf:/etc/mysql/conf.d/:ro
- mysqldata:/var/lib/mysql
- ./db/logs:/var/log/mysql
#使用するネットワーク
networks:
- backend
api:
image: node:14.16.0-buster
environment:
- MYSQL_SERVER=db
- MYSQL_USER=${DB_USER}
- MYSQL_PASSWORD=${DB_PASS}
- MYSQL_DATABASE=${DB_NAME}
- TZ=${TZ}
- CHOKIDAR_USEPOLLING=true
#コンテナを起動させ続けるよう設定
tty: true
ports:
- '3000:3000'
# ソースコードを格納するフォルダをマウント
#(ホスト側の./apiをコンテナの/appにマウント)
volumes:
- ./api:/app
# 起動時のカレントフォルダを指定
working_dir: /app
# 起動後に実行するコマンドを指定
command: npm run dev
networks:
- backend
#依存関係(apiコンテナより先にdbコンテナが起動するように設定)
depends_on:
- db
vue:
image: node:14.16.0-buster
environment:
- CHOKIDAR_USEPOLLING=true
tty: true
ports:
- '8080:8080'
volumes:
- ./vue:/app
working_dir: /app
command: npm run serve
networks:
- backend
depends_on:
- api
networks:
backend:
volumes:
mysqldata:
db
、api
、vue
の3つのサービスを定義しています。
###イメージimage:
イメージはMySQLとNode.jsの公式イメージをそのまま使用しています。(今回はDockerfileを使いません。)Node.jsイメージはnode:14.16.0-buster
を使用しています。なお、buster
というのはDebianのVer.10のことです。ちなみにDebianのバージョン名は映画「トイ・ストーリー」の登場キャラクターが元になっているそうです。バージョン14.16.0
は記事執筆時点での最新のLTS版を使用しています。
###環境変数environment:
db
コンテナとapi
コンテナには、MySQLを使用(MySQLに接続)するための環境変数を設定していますが、後述する.env
ファイルで定義したものを取り込むようにしています。直接docker-compose.ymlファイルに記述しなかったのは、データベースの接続情報をソースに含めるのはよろしくないからです。Githubに公開するときは.gitignore
に.env
ファイルを追加し、ソース管理から除外しましょう。
api
コンテナとvue
コンテナの環境変数には- CHOKIDAR_USEPOLLING=true
を設定しています。これを書かないと、apiコンテナに入れるソースコードの変更を検知して自動でアプリを再起動するライブラリ「nodemon」が正常に動作しません。一応vue
コンテナにも書きました。
参考:Docker 環境で nodemon が watch してくれない問題と対処方法 - Qiita
###ポートports:
ユーザーからリクエストを受けるvue
コンテナの8080番ポートと、ホスト側の8080番ポートを対応させています。
残りの2つのコンテナはコンテナ間のみで通信できればいいので、Dockerホストにマッピングする必要はないですが、api
コンテナもホストから動作を確認したいので設定しています。api
コンテナの3000番ポートと、ホスト側の3000番ポートを対応させています。(なお、db
コンテナも対応付けていますが、これは意味ないです。)
###ボリュームvolumes:
db
コンテナでは、ホスト側のtodo_app/db/conf
ディレクトリをコンテナの/etc/mysql/conf.d/
ディレクトリにバインドマウントして、MySQLのデフォルト設定を記述したmy.cnf
ファイル(後述)を上書きしています。
また、MySQLのデータはコンテナが削除されてもデータが消えないように、mysqldata
というボリュームを作成し、コンテナの/var/lib/mysql
ディレクトリにボリュームマウントしています。(Windows環境ではバインドマウントだと上手くいかないそうです。)
さらに、MySQLのログデータを格納しているディレクトリもバインドマウントすることで、ホストからログを確認できるようにしています。
api
コンテナでは、ホスト側のtodo_app/api
をapiコンテナの/app
にバインドマウントすることで、アプリケーションのファイル群をホスト側から編集可能にしています。
vue
コンテナでも同様です。(ホスト側のtodo_app/vue
をvueコンテナの/app
にバインドマウント)
###コマンドcommand:
api
コンテナおよびvue
コンテナでは、起動時に実行するコマンドを設定しています。しかし、現時点ではこれらのコマンドを動作させるために必要なライブラリがインストールされていないため、コンテナを起動してもすぐに終了してしまいます。
###ネットワークnetworks:
コンテナ名でコンテナ間の通信を行うために、backend
という名前のDockerネットワークを作り、db
コンテナ、api
コンテナ、vue
コンテナで設定しています。
###依存関係depends_on:
依存関係を設定しました。db
→api
→vue
コンテナの順で起動します。停止するときは逆順になります。vue
コンテナ(Todoリストアプリ)はapi
コンテナの提供するAPIを使用しないとデータのCRUD(作成・取得・更新・削除)ができません。つまり、vue
コンテナはapi
コンテナに依存していると言えます。そのapi
コンテナも、データをdb
コンテナに取りにいかなければなりません。api
コンテナはdb
コンテナに依存していると言えます。
my.cnfの編集
todo_app/db/conf/my.cnf
を以下のように編集します。
# MySQLサーバーへの設定
[mysqld]
# 文字コード/照合順序の設定
character-set-server = utf8mb4
collation-server = utf8mb4_bin
# タイムゾーンの設定
default-time-zone = SYSTEM
log_timestamps = SYSTEM
# デフォルト認証プラグインの設定
default-authentication-plugin = mysql_native_password
# エラーログの設定
log-error = /var/log/mysql/mysql-error.log
# スロークエリログの設定
slow_query_log = 1
slow_query_log_file = /var/log/mysql/mysql-slow.log
long_query_time = 5.0
log_queries_not_using_indexes = 0
# 実行ログの設定
general_log = 1
general_log_file = /var/log/mysql/mysql-query.log
# mysqlオプションの設定
[mysql]
# 文字コードの設定
default-character-set = utf8mb4
# mysqlクライアントツールの設定
[client]
# 文字コードの設定
default-character-set = utf8mb4
ここでは、文字コードの設定や、ログの出力先設定などを行っています。
また、デフォルトの認証プラグインを変更しています。
なお、DockerホストがWindowsの場合、todo_app/db/conf/my.cnf
は読み取り専用にしておきます。
参考:[Docker+Windows]mysqlのdockerイメージがmy.cnfのマウントのエラーで起動しない時の対処法 - Qiita
.envファイルの編集
todo.app/.env
ファイルを以下のように編集します。
docker-compose.ymlでMySQLの環境変数を設定しますが、このファイルから具体的な値を取得しています。
参考:docker-composeのenv_fileと.envファイルの違い - Qiita
DB_ROOT_HOST=%
DB_NAME=todo
DB_USER=username
DB_PASS=mypassword
TZ=Asia/Tokyo
apiコンテナを起動してコンテナ内でExpress.jsアプリケーションを作成する
api
コンテナを起動して、express.jsアプリケーションの雛型を作成していきます。
docker-compose.ymlが置いてあるディレクトリ(todo_app/
)で次のコマンドを実行します。
#docker-compose.ymlが置いてあるディレクトリに移動
cd todo_app
#apiコンテナを一時的に起動
$ docker-compose run --rm --no-deps api /bin/bash
このコマンドは、api
コンテナを実行(run
)し、シェルを起動する(/bin/bash
)という意味になります。
--rm
は、実行後に自動でコンテナを削除するオプションです。
--no-deps
は、リンクしたサービスを起動しないようにするオプションです。docker-compose.ymlでコンテナの依存関係を定義したため、本来ならばapi
コンテナが起動する前にdb
コンテナが立ち上がるはずですが、このオプションを設定することでapi
コンテナのみ起動するようにしています。
lsコマンドで/app
ディレクトリ内に何もないことを確認しておきましょう。
==== ここからapiコンテナ内 ====
# lsコマンドで、/appディレクトリ(=ホスト側のtodo_app/api)内にまだ何もないことを確認
ls -al
total 4
drwxrwxrwx 1 node node 4096 Feb 15 11:48 .
drwxr-xr-x 1 root root 4096 Feb 15 11:46 ..
私の環境ではnpmのバージョンを上げておかないとライブラリのインストールが失敗してしまうことがあったので、npmをアップデートしておきます。(今後も何度かこの操作を行うのでDockerfileに書いておくべきだったかもしれません…。)
==== apiコンテナ内 ====
# npmのバージョンを確認
npm -v
6.14.11
# npmのアップデート
npm install -g npm
# npmがアップデートされたことを確認
npm -v
7.6.1
npm install
でexpress-generatorをインストールして実行します。
(なぜかnpx express-generator
では上手くいきませんでした。npmのバージョンを上げなければうまくいくのですが…)
==== apiコンテナ内 ====
# express-generatorをインストールして実行
npm install -g express-generator
express .
nodemonをインストール。ソースコードを変更したときに、自動でサーバーを再起動してくれる便利なツールです。開発環境でのみ使用するので-D
(--save-dev
)オプションを付けておきます。
==== apiコンテナ内 ====
# nodemonをインストール
npm install -D nodemon
exitでコンテナを抜けます。
==== apiコンテナ内 ====
# exitでコンテナを抜ける
exit
==== ここまでapiコンテナ内 ====
todo_app/api/package.json
を編集して、"scripts"
に"dev"
を追加します。
{
"name": "app",
"version": "0.0.0",
"private": true,
"scripts": {
"dev": "nodemon ./bin/www",
"start": "node ./bin/www"
},
"dependencies": {
"cookie-parser": "~1.4.4",
"debug": "~2.6.9",
"express": "~4.16.1",
"http-errors": "~1.6.3",
"jade": "~1.11.0",
"morgan": "~1.9.1",
"mysql2": "^2.2.5",
"sequelize": "^6.5.0",
"sequelize-cli": "^6.2.0"
},
"devDependencies": {
"nodemon": "^2.0.7"
}
}
以下のコマンドで3つのコンテナをまとめて起動します。
$ docker-compose up -d
起動後、apiコンテナはnpm run dev
が自動的に実行されます。(docker-compose.ymlで定義しました。)
http://localhost:3000/に接続して、Expressの初期画面が表示されることを確認します。
ここで、docker-compose ps
コマンドでコンテナの起動状況を確認しておきます。
$ docker-compose ps
vueコンテナだけ状態(State
)がExit
になっています。(停止状態になっています。)
これは、vueコンテナを起動時に実行されるコマンド(npm run serve
)の実行に失敗したためです。後ほどvueコンテナに入りVue CLIをインストールすることで、このコマンドを実行できるようになります。
dbコンテナを起動して正常に設定されていることを確認
起動済みのdbコンテナに入り、MySQLにログインする。
以下のコマンドを実行し、dbコンテナのシェルを起動してMySQLにログインします。
$ docker-compose exec db /bin/bash
==== ここからdbコンテナ内 ====
mysql -u root -p
MySQLログイン時のパスワードは、.env
ファイルで指定したものを入力してください。(今回はmypassword
)
MySQLの文字コードを確認
MySQLにログイン出来たら、念のため文字コードの設定を確認します。
==== dbコンテナ内 ====
mysql> show variables like 'char%';
todo_app/db/conf/my.cnf
で指定した通りの設定になっていればOKです。
データベースを確認
データベースの状態を確認します。
==== dbコンテナ内 ====
mysql> show databases;
環境変数で指定したtodo
というデータベースが存在しているのがわかります。
todo
データベースの中身はどうなっているでしょうか?
==== dbコンテナ内 ====
mysql> use todo;
mysql> show tables;
todo
データベースの中身は空です。(テーブルは1つも存在しません。)
テーブルの作成は手作業でもできますが、今回はapiコンテナからSequelizeというライブラリのDBマイグレーション機能を使用して行います。
quit
でMySQLを抜け、exit
でdbコンテナも抜けてください。
==== dbコンテナ内 ====
mysql> quit
exit
==== ここまでdbコンテナ内 ====
apiコンテナ内で、sequelizeとその依存パッケージをインストール
今度はapiサーバーを設定していきます。
apiコンテナのシェルを立ち上げ、npmのバージョンをアップデートしておきます。
$ docker-compose exec api /bin/bash
==== apiコンテナ内 ====
# npmのバージョンを確認
npm -v
6.14.11
# npmのアップデート
npm install -g npm
# npmがアップデートされたことを確認
npm -v
7.6.1
まず、APIコンテナ内でSequelizeとその依存パッケージをインストールします。
==== apiコンテナ内 ====
npm install mysql2 sequelize sequelize-cli
次に、sequelize-cliを使用してSequelizeの初期化を行います。
==== apiコンテナ内 ====
npx sequelize-cli init
これによって、apiフォルダ内にconfig
、migrations
、models
、seeders
の4つのディレクトリが作成されます。
次に、以下のコマンドでモデルクラスを生成します。
==== apiコンテナ内 ====
npx sequelize-cli model:generate --name Task --attributes name:string,done:boolean
上記の例では、string型のname
と、boolean型のdone
という2つのプロパティを持つ、Task
というモデルクラスを作成しています。
カラム名 | 型 | 説明 |
---|---|---|
name | string | タスク名 |
done | boolean | タスクが終了したか否か |
実行すると、todo_app/api/models
ディレクトリに、task.js
ファイルが出来ているのがわかります。
'use strict';
const {
Model
} = require('sequelize');
module.exports = (sequelize, DataTypes) => {
class Task extends Model {
/**
* Helper method for defining associations.
* This method is not a part of Sequelize lifecycle.
* The `models/index` file will call this method automatically.
*/
static associate(models) {
// define association here
}
};
Task.init({
name: DataTypes.STRING,
done: DataTypes.BOOLEAN
}, {
sequelize,
modelName: 'Task',
});
return Task;
};
また、同時にtodo_app/api/migrations
ディレクトリに、{日時}-create-task.js
というファイルも出来ています。
'use strict';
module.exports = {
up: async (queryInterface, Sequelize) => {
await queryInterface.createTable('Tasks', {
id: {
allowNull: false,
autoIncrement: true,
primaryKey: true,
type: Sequelize.INTEGER
},
name: {
type: Sequelize.STRING
},
done: {
type: Sequelize.BOOLEAN
},
createdAt: {
allowNull: false,
type: Sequelize.DATE
},
updatedAt: {
allowNull: false,
type: Sequelize.DATE
}
});
},
down: async (queryInterface, Sequelize) => {
await queryInterface.dropTable('Tasks');
}
};
この時点のフォルダ構成は次の通りです。
sequelize-cliでDBマイグレーションを実行
次は、DBマイグレーションを実行してtodo
データベースにテーブルを作成します。
しかし、その前にDB接続情報を正しく設定する必要があります。
先程npx sequelize-cli init
でSequelizeの初期化を行った時に、todo_app/api/config
ディレクトリにconfig.json
というJSONファイルが出来ているはずですが、それをconfig.js
にしてJavascriptファイルにします。
そして内容を次のように書き換えます。
これは、docker-compose.ymlで設定した環境変数の値をそのままDB接続情報として使うためです。
module.exports = {
development: {
username: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
host: process.env.MYSQL_SERVER,
dialect: 'mysql',
},
test: {
username: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
host: process.env.MYSQL_SERVER,
dialect: 'mysql',
},
production: {
username: process.env.MYSQL_USER,
password: process.env.MYSQL_PASSWORD,
database: process.env.MYSQL_DATABASE,
host: process.env.MYSQL_SERVER,
dialect: 'mysql',
},
};
また、todo_app/api/models/index.js
ファイルを開き、config.json
となっている部分をconfig.js
に変更します。
この状態で、以下のコマンドを実行するとSequelizeによるDBマイグレーションが実行されます。
つまり、何もなかったtodo
データベースにTasks
テーブルが作成されます。
==== apiコンテナ内 ====
npx sequelize-cli db:migrate
最後に、exit
でapiコンテナを抜けましょう。
==== apiコンテナ内 ====
exit
==== ここまでapiコンテナ内 ====
Tasksテーブルが作成されたことを確認
todo
データベースにTasks
テーブルが作成されたことを確認しておきます。
以下のコマンドでdbコンテナのシェルを実行してください。
docker-compose exec db /bin/bash
先程と同様に、MySQLにログイン後、todo
データベースに切り替え、テーブルを見てみます。
==== ここからdbコンテナ内 ====
mysql -u root -p
mysql> use todo
mysql> show tables;
結果、Tasks
テーブルが出来ていることが確認できました。なお、初回はマイグレーション管理用にSequelizeMeta
テーブルも作られます。
ではMySQLとdbコンテナを抜け、ホストに戻ります。
==== dbコンテナ内 ====
mysql> quit
exit
==== ここまでdbコンテナ内 ====
vueコンテナの準備
ここで、ユーザーからのリクエストを受け取るvueコンテナの準備をしていきます。以前も触れましたが、vueコンテナは起動時に実行されるはずのnpm run serve
コマンドが実行できなかったため、停止しています。(docker-compose ps
でもう一度確認してみましょう。)
ここでは、Vue CLIをインストールし、npm run serve
コマンドを使えるようにしていきます。
vueコンテナは停止しているため、docker-compose exec
は使えません。ここでは、docker-compose run
を使ってvueコンテナを一時的に起動します。
docker-compose run --rm --no-deps vue /bin/bash
例によってnpmのバージョンをアップデートしておきます。
==== vueコンテナ内 ====
# npmのバージョンを確認
npm -v
6.14.11
# npmのアップデート
npm install -g npm
# npmがアップデートされたことを確認
npm -v
7.6.1
npmのアップデート後、Vue CLIをインストールします。
==== vueコンテナ内 ====
npm install -g @vue/cli
インストール完了後、現在のディレクトリ.
を対象にvue create
を行います。
==== vueコンテナ内 ====
vue create .
以下の画像を参考に進めていきます。
↑デフォルトで参照しているレジストリでは接続が遅く、「こっち(https://registry.npm.taobao.org
)の方が早いからこっちを使おうよ!」と聞いているようですが、よくわからないのでNo(n
)を選択しました。ちなみにtaobaoというのは中国のサーバーらしいです。
↑カレントディレクトリに作っていいのか聞いています。Yes(Y
)で大丈夫です。
↑プリセットを選択します。Default (Vue 3 Preview) ([Vue 3] babel, eslint)
を選びました。
↑パッケージマネージャーを選択します。NPM
を選びました。
その後、待っていればvue create
が終わります。(私の環境だと結構時間がかかりました…)
次に、apiコンテナ(APIサーバー)からデータの取得や更新を行うために、axiosをインストールします。
==== vueコンテナ内 ====
npm install axios
インストールが完了したら、exit
でvueコンテナを抜けます。
==== vueコンテナ内 ====
exit
==== ここまでvueコンテナ内 ====
そして、docker-compose down
で全てのコンテナを停止&削除した後、docker-compose up -d
で再び起動し直します。
docker-compose down
docker-compose up -d
ここで、docker-compose ps
で各コンテナのステータスを確認してみると、今度はvueコンテナも稼働状態になっていることがわかります。Vue CLiをインストールしたことで、npm run serve
コマンドが実行できるようになりました。
docker-compose ps
http://localhost:8080/に接続し、Vue.jsの初期画面が表示されることを確認します。
APIの実装
ここからは、apiコンテナ内のプログラムを編集し、ToDoリストアプリのCRUDのAPIを実装していきます。
まず、todo_app/api/routes/index.js
に2行追加します。
/task
ルートの処理をtaskRoutes.js
に投げるようにします。
var express = require('express');
var router = express.Router();
//この2行を追加する
const taskRoutes = require("./taskRoutes");
router.use("/task", taskRoutes);
/* GET home page. */
router.get('/', function (req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
次に、todo_app/api/routes/
に、ファイルtaskRoutes.js
を作成して、以下のように編集します。
"use strict";
const router = require("express").Router(),
taskController = require("../controllers/taskController");
router.get("/", taskController.read);
router.post("/", taskController.create);
router.put("/:id", taskController.update);
router.delete("/:id", taskController.delete);
module.exports = router;
ここでは、4つのhttpメソッド(get、post、put、delete)毎に違う処理を割り当てています。
処理の内容は、todo_app/api/controllers/taskController.js
内に記述していきます。
todo_app/api/
に、controllers
という名前のディレクトリを作成し、その中にtaskController.js
を作成します。
todo_app/api/controllers/taskController.js
は、以下のように編集します。
"use strict";
const db = require("../models/index");
module.exports = {
read: async(req, res, next) => {
try{
const result = await db.Task.findAll();
res.send(result);
}catch(err){
res.status(500).send(err);
}
},
create: async(req, res, next) => {
try{
const result = await db.Task.create({
name: req.body.name,
done: false
});
res.send(result);
}catch(err){
res.status(500).send(err);
}
},
update: async(req, res, next) => {
try{
const result = await db.Task.update(
{
name: req.body.name,
done: req.body.done
},
{
where: {
id: req.params.id
}
}
);
res.send(result);
}catch(err){
res.status(500).send(err);
}
},
delete: async(req, res, next) => {
try{
const result = await db.Task.destroy({
where: {
id: req.params.id
}
});
res.send({
result: result
});
}catch(err){
res.status(500).send(err);
}
}
}
これで、APIコンテナの実装は完了です。
Vue.jsの実装
最後に、フロントエンドの実装を行います。
デフォルトで存在するtodo_app/vue/src/components/HelloWorld.vue
というコンポーネントを以下のように書き換えます。
<template>
<div class="hello">
<form>
<input type="text" style="display: none" />
<input v-model="currentTask" type="text" />
<input type="button" value="add!" @click="taskCreate" />
</form>
<table align="center" border="0">
<tr>
<th>done</th>
<th>task</th>
<th>delete</th>
</tr>
<tr v-for="(task, index) in tasks" :key="task.id">
<td>
<input
type="checkbox"
v-model="task.done"
@change="taskUpdate(task.id, task.name, task.done)"
/>
</td>
<td>
<input
type="text"
v-model="task.name"
@change="taskUpdate(task.id, task.name, task.done)"
/>
</td>
<td>
<input type="button" value="delete" @click="taskDelete(task.id, index)" />
</td>
</tr>
</table>
</div>
</template>
<script>
import axios from "axios";
export default {
name: "HelloWorld",
data: () => ({
tasks: [],
currentTask: "",
}),
created: async function () {
try {
const result = await axios.get("/task/");
this.tasks = result.data;
} catch (err) {
alert(JSON.stringify(err));
}
},
methods: {
taskCreate: async function () {
try {
const result = await axios.post("/task/", {
name: this.currentTask,
});
this.tasks.push(result.data);
this.currentTask = "";
} catch (err) {
alert(JSON.stringify(err));
}
},
taskDelete: async function (id, index) {
try {
await axios.delete("/task/" + id);
this.currentTask = "";
this.tasks.splice(index, 1);
} catch (err) {
alert(JSON.stringify(err));
}
},
taskUpdate: async function (id, val, done) {
try {
await axios.put("/task/" + id, {
name: val,
done: done,
});
this.currentTask = "";
} catch (err) {
alert(JSON.stringify(err));
}
},
},
};
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h3 {
margin: 40px 0 0;
}
ul {
list-style-type: none;
padding: 0;
}
li {
margin: 0 10px;
}
a {
color: #42b983;
}
.table {
height: 100%;
text-align: center;
}
</style>
次に、todo_app/vue/
にファイルvue.config.js
を作成し、以下のように編集します。
参考:Vue.jsとAPIサーバとのaxiosでCORSに引っかかった時のProxyを使った回避方法 - Qiita
module.exports = {
devServer: {
proxy: 'http://api:3000'
}
};
編集し終わったら、ここで各コンテナを再起動しましょう。
docker-compose down
docker-compose up -d
これで、ToDoリストアプリが完成しているはずです。http://localhost:8080/にアクセスして操作してみましょう。
出来ました!
参考書籍
-
さわって学ぶクラウドインフラ docker基礎からのコンテナ構築
- わかりやすい解説で理解しやすかったです。
主な参考サイト
- DockerでNode.jsアプリケーションを開発する (1) Express.jsをコンテナ内で動かす - Ishida-IT LLC
- DockerでNode.jsアプリケーションを開発する (3) MySQL用コンテナを追加 - Ishida-IT LLC
- [Vue.js + Express + Sequelize + DockerでCRUD搭載のTodoリストを作ってみる - Qiita]
(https://qiita.com/yoshiplum/items/129e7ad1ffc3a02b9eb2)