LoginSignup
4
4

More than 3 years have passed since last update.

[Node.js勉強会]ExpressフレームワークでTODOアプリを作ろう

Last updated at Posted at 2020-03-30

この記事について

Node.jsの概念や、Expressの使い方について詳しく書いてある記事ではありません。
ある程度の土台が用意してあるので、それを元に実際に手を動かして、TODOアプリを完成させることがこの記事の目的です。

pugファイルと仮処理を記述 の部分までは、コピペで進めていただいても大丈夫です。
TODOの処理を記述 の部分から実際に、Node.jsの様々な書き方を試していただけたらと考えております

今回作成する、TODOアプリの動作は以下のような形です。
express-comp.mov.gif

Node.jsとは

Node.js はスケーラブルなネットワークアプリケーションを構築するために設計された非同期型のイベント駆動の JavaScript 環境です。
Node.js公式

非同期で処理が実行されるので、DBとの通信のような時間がかかる処理を実行する場合は async, await の記述が必要です。

Expressとは

Node.js のための高速で、革新的な、最小限のWebフレームワーク
Express公式

最小限の機能を持ったフレームワークです。基本的に利用するのは、ルーティングの機能かと思います。
LaravelやRailsのようなフレームワークの場合、プロジェクトを作成した時点でログイン機能、メール機能、など様々な機能が簡単に実装出来るように用意されていますが、Expressは全て自分でライブラリを選定して実装しないといけません。
そのため、フレームワーク自体の容量がとても小さいです。

事前準備

・ Git
・ Docker

手順1 dockerを利用して、Expressの実行環境を整える

スムーズにNode.jsの学習を進めるために、開発環境の構築をDockerで行います。

$ git clone https://github.com/KazukiSadasue/express-mysql-sample.git
$ cd express-mysql-sample
$ docker-compose up -d

上記のコマンドで、dockerコンテナが起動して、サーバーも立ち上がります。
http://localhost
に接続して、以下の画像のように表示されたら実行環境は整いました。
*もしかしたら、最初の1回目にサーバーの立ち上げ失敗するかもしれません。
その場合は、docker-compose restart で再度立ち上げると治ると思います。

express-first-step.png

手順2 作成するアプリケーションについて

簡単なTODOアプリケーションを作成します。
ルーティングは以下のようにしようと思います。

パス メソッド 内容
/ GET TODOトップ
/create POST TODO作成
/done POST TODO完了
/delete POST TODO削除

TODOテーブルは以下のようにします。

カラム 説明
id INTEGER PRIMARYキー
content STRING TODO内容(必須)
isDone Boolean TODOの状態(必須、デフォルト0)
deletedAt DATETIME 削除日時
createdAt DATETIME 作成日時
updatedAt DATETIME 更新日時

モデルの作成

テーブル設計から、TODOモデルを作成します。
modelsフォルダにtodo.jsを作成して、中身は以下のようにします。


todo.jsのコード
todo.js
'use strict';
const loader = require('./sequelize-loader');
const Sequelize = loader.Sequelize;

const Todo = loader.database.define(
  'todos',
  {
    id: {
      type: Sequelize.INTEGER,
      primaryKey: true,
      autoIncrement: true,
      allowNull: false,
    },
    task: {
      type: Sequelize.STRING,
      allowNull: false,
    },
    isDone: {
      type: Sequelize.BOOLEAN,
      allowNull: false,
      defaultValue: 0,
    },
  }, {
    paranoid: true,
    freezeTableName: true,
  }
);

module.exports = Todo;


次にapp.jsでモデルを読み込みます。

app.js
var logger = require('morgan');
var helmet = require('helmet');

// モデルの読み込み
+var Todo = require('./models/todo');
+Todo.sync();

モデルの作成が終わったら、Dockerコンテナを再度立ち上げます。

$ docker-compose restart

サーバーが再起動して、モデルに対応するテーブルが作成されます。
テーブルを確認するには、以下のコマンドを実行

$ docker exec -it mysql bash
# mysql -u root -p
Enter Password: パスワードは入力せずにEnter

mysql> use express-sample
mysql> show tables;
+--------------------------+
| Tables_in_express-sample |
+--------------------------+
| todos                    |
+--------------------------+

todosテーブルが作成されているのが確認出来ました!

pugファイルと仮処理を記述

TODOアプリのデザインはBootstrapを用いて作成しました。
以下のコードをコピペして配置してください。


views/index.pugのコード
views/index.pug
extends layout

block content
  h1 TODOアプリ
  div
  form(action="/create", method="post").form-inline.mb-3
    div.form-group
      input(type="text" name="task" placeholder="例: 晩御飯を作る").form-control
    button(type="submit").btn.btn-primary 追加

  div.mb-3
    h2 TODO
    form(name="todoform" method="post").mb-2
      ul.list-group
      each todo in todoTasks
        li.list-group-item.px-5
          input(name="todos", value=todo.id, type="checkbox", id=`todo-${todo.id}`).form-check-input
          label(for=`todo-${todo.id}`).form-check-label #{todo.task}
    if todoTasks.length
      button.btn.btn-success(onclick="done()").mr-2 達成
      button.btn.btn-danger(onclick="deleteTodo()") 削除
    else
      div TODOタスクはありません


  div
    h2 DONE
    form(name="doneform" method="post").mb-2
      ul.list-group
      each done in doneTasks
        li.list-group-item.px-5
          input(name="todos", value=done.id, type="checkbox", id=`todo-${done.id}`).form-check-input
          label(for=`todo-${done.id}`).form-check-label #{done.task}
    if doneTasks.length
      button.btn.btn-danger(onclick="deleteDone()") 削除
    else
      div DONEタスクはありません

  script(src="javascripts/todo.js")



puglic/javascripts/todo.jsのコード
public/javascripts/todo.js
function done() {
  document.todoform.action="/done";
  document.todoform.submit();
}

function deleteTodo() {
  document.todoform.action="/delete";
  document.todoform.submit();
}

function deleteDone() {
  document.doneform.action="/delete";
  document.doneform.submit();
}



routes/index.jsのコード
routes/index.js
var express = require('express');
var router = express.Router();

// TODOトップページ
router.get('/', async (req, res, next) => {
  const todoTasks = [];
  const doneTasks = []

  res.render('index', {
    todoTasks, doneTasks,
  });
});

module.exports = router;


コードの記述が終わったら、 docker-compose restart コマンドでコンテナを立ち上げなおします。

すると、以下のような状態になるかと思います。
express-second-step.png

TODOの処理を記述

ここまで見た目の部分と、モデルの定義まで終わりました。
あとは、routes/index.js の部分に処理を書き加えるだけでTODOアプリが完成します。
ここから先は、色々と手を動かしてNode.jsの書き方に触れていただけたらと思います。

基本的な使い方

// ライブラリや、モデルの読み込みには、requireを利用する
const { Op } = require('sequelize');

// getのルーティング
router.get('/', () => {});
// postのルーティング
router.post('/', () => {});

Sequelizeの使い方

モデルの使い方は、 Sequelize公式ドキュメント を参考にします。
使い方の一部を抜粋してこちらに記述します。

// async を記述することで、非同期関数を定義する。
router.get('/', async (req, res, next) => {
  // isDoneがtrueのデータを全件取得
  // awaitを記述することで、DB処理の結果が返ってくるまで待つ
  const todos = await Todo.findAll();
})

Sequelizeの使う上で、Node.jsは非同期通信であることに注意しないといけません。
async, awaitを利用して、DB処理が終わってから次の処理を行うようにしないと、不具合の原因となってしまいます。

上記の内容を参考に、TODOアプリを完成させていただけたらと思います。

最後に

TODOアプリ完成しましたでしょうか?
最終的な私の実装例を記述致します。


routes/index.jsの実装例
routes/index.js
var express = require('express');
var router = express.Router();
const Todo = require('../models/todo');
const { Op } = require('sequelize');

// TODOトップページ
router.get('/', async (req, res, next) => {
  const todos = await Todo.findAll();
  const todoTasks = todos.filter(todo => todo.isDone === false);
  const doneTasks = todos.filter(todo => todo.isDone === true);

  res.render('index', {
    todoTasks, doneTasks,
  });
});

// TODO作成
router.post('/create', async (req, res, next) => {
  await Todo.create({
    task: req.body.task,
  });
  res.redirect('/');
});

// TODO完了
router.post('/done', async (req, res, next) => {
  const todoModels = await getModelsByIds(req.body.todos);
  for(const todo of todoModels) {
    await todo.update({
      isDone: true
    });
  }
  res.redirect('/');
});

// TODO削除
router.post('/delete', async (req, res, next) => {
  const todoModels = await getModelsByIds(req.body.todos);
  for(const todo of todoModels) {
    await todo.destroy();
  }
  res.redirect('/');
});

// リクエストのIDからTODOモデルを取得する
async function getModelsByIds(ids) {
  if (typeof ids === 'string') {
    ids = ids.split();
  }
  return await Todo.findAll({
    where: {
      id: {
        [Op.in]: ids,
      }
    }
  });
}

module.exports = router;


もっと説明の記述が必要、xxが分からない等ありましたらコメントお願い致します!

今回のコードの配置場所
https://github.com/KazukiSadasue/express-mysql-sample

4
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
4