Node.js基本編 Express+SQLiteで超定番のTo Doメモアプリを作る

本編はNode.js+Express+SQLiteを使って、DataBaseにCRUDできるアプリを作る工程を紹介をします。実際に作るのは定番中の定番、To Doメモアプリですので難しい事は一切しない初学者向けです。

掌田津耶乃 著の[Node.js超入門](https://www.shuwasystem.co.jp/book/9784798055220.html)がベースです。

## 一応念のため、CRUDとは
Create(登録) 新しいデータを登録する。
Read(参照) レコードをテーブルから取り出す。 
Update(更新) レコードの内容を更新する。 
Delete(削除) レコードを削除する。

## 一応念のため、Webアプリケーションとは（サイトとの違い）
Webサイト＝情報を提供
静的ファイルをホスティングしてるだけで、誰が見ても同じ情報を表示する。

Webアプリケーション＝情報のやり取り
ユーザーがフォーム入力、送信、など様々な行動をアプリケーション上で行える。ユーザーが送信したデータを処理して結果を表示する。



#### では実際にやっていきましょう。大きく分けて4ステップで説明していきます。
**Step1：**Node.jsアプリ基盤構築
**Step2：**DataBase構築
**Step3：**ページを用意
**Step4：**CRUD処理を実装

前提条件：**Node.js**はインストールしておいてください。インストールの仕方は[こちら](https://qiita.com/sefoo0104/items/0653c935ea4a4db9dc2b)を参考

## Step1：Node.jsアプリ基盤構築
入門編でも紹介したアプリケーションのひな形を生成してくれる便利なツールのExpress-Generatorをインストールしましょう。

```
npm install -g express-generator
```
これでグローバルにインストールされてシステム全体でExpress-Generatorが使えます。

ではアプリを作っていきます。 `express -e アプリ名`

```
express -e todo-app
```

これで「todo-app」という名前のディレクトリができ、各種フォルダも自動で作られます。
![スクリーンショット 2021-05-01 19.23.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/614ff66a-01dc-f53e-6af1-88fcf842194b.png)

Express-Generatorはデフォルトで各種パッケージを入れておいてくれるので、プロジェクトディレクトリに入り、npm installをして全部インストールしましょう。

```
cd todo-app
npm install
```

そして、今回DataBaseを扱う為に必要なSQLite3は、別途インストールが必要です。

```
npm install --save sqlite3
```

各ディレクトリの説明をざっくりと、
**bin/**
アプリを実行する為のコマンドとなるファイルが保管されてます。基本的に触りません。

**public/**
CSSや画像などをこの中に置きます。

**routes/**
このディレクトリではルート（URL）ごとの処理がまとめらています。
用意するページのアドレスごとにここにファイルを追加します。

**views/**
画面側を作る上で必要なViewファイルを置きます。サーバーサイドからこのファイルに対して値を渡すことが出来ます。

**node_modules/**
npm installするとパッケージ類はこのディレクトリに入ってきます。基本的に触りません。

**app.js**
これがメインプログラムです。Expressの設定周りを担っています。


これでアプリの基盤はひとまず完成です。**Express-Generator**を使うとすごい楽ですね。



## Step2：DataBase構築
続いてDataBaseの構築です。DB Browser for SQLiteを使います。
`*DB Browser for SQLiteはSQLiteのデータベースをGUIで管理することができるツールです。`　導入方法は[こちら](https://www.dbonline.jp/sqlite-db-browser/)を参考にしてください、簡単です。

**まずDataBaseファイルを作ります。**
「新しいデータベース」をクリックします
ファイル名は「memo_data」として、保存先はtodo-appフォルダ直下です。これで`memo_data.db / memo_data.sqlite3`というファイルが作られます。

**次にテーブルを作ります。**
今回は超シンプルなメモアプリなので、メモを格納するテーブル(テーブル名：memos)を一つだけ用意ます。

**次にカラムを作ります**
カラムは以下2点に留めておきます。
・id -これは編集、削除するのに指定する為に必須
・text
（もっとあっていいですが今回はミニマムな実装に留めます）
![スクリーンショット 2021-05-02 22.59.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/fb7270b9-f184-2887-fb1a-93e56cfa1ce0.png)


最初に何個かダミーデータを登録しておきましょう。


普通のアプリ開発では必要なデータを洗い出して、テーブル、カラムをどう分けるかなどをこんな風に図に起こして **設計** するのが当たり前ですが、
![スクリーンショット 2021-05-02 5.33.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/e9373d16-3b1a-1d3d-0ec2-d2337defd932.png)
今回は超絶シンプルなメモアプリなので設計なんて大そうな作業は不要です。

ちなみにテーブル、カラムなど超基本のデータベース用語ですが、カラムが列（項目）で、テーブルがそれをまとめる表のイメージです。
![スクリーンショット 2021-05-02 5.14.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/fbaeb955-5886-e630-d772-d7156fae4b58.png)

データベース構築はこれで完了です。


## Step3：ページを用意
続いてクライアント側の画面であるページを作っていきます。
Node.jsではテンプレートエンジンというクライアント側の画面が簡単に作成できるパッケージがあります。Express-Generatorで既にejsというテンプレートエンジンがインストール済みなので、これを使ってページを作っていきます。

今回必要なページは４つです。views配下に`memo`という名前でフォルダを作り、
**メモ一覧表示ページ：** index.ejs 
**新規メモ追加ページ：** add.ejs
**メモ編集ページ：** edit.ejs
**メモ削除ページ：** delete.ejs
を作成してください、中身のコードは以下になります。

index.ejs 

```html
<!DOCTYPE html>
<html>
  <head>
    <title><%= title %></title>
  </head>
  <body>
    <h3><%= title %> <a href="/memo/add">追加</a></h3>
    <div>
      <table>
      <% for(var i in content) { %>
        <tr>
          <% var obj = content[i]; %>
          <td><%= obj.text %></td>
          <td><a href="/memo/edit?id=<%= obj.id %>">更新</a></td>
          <td><a href="/memo/delete?id=<%= obj.id %>">削除</a></td>
        </tr>
      <% } %>
      </table>
    </div>
  </body>
</html>
```

add.ejs(index.ejsとbodyタグ以外は同じ)

```html
  <body>
    <h3><%= title %></h3>
    <p><%= content %></p>
    <div>
      <form action="/memo/add" method="post">
        <p>メモ内容：<input type="text" name="text"></p>  
        <input type="submit" value="追加">
      </form>
    </div>
  </body>
```

edit.ejs(index.ejsとbodyタグ以外は同じ)

```html
  <body>
    <h3><%= title %></h3>
    <p><%= content %></p>
    <div>
      <form action="/memo/edit" method="post">
        <p>メモ内容：<input type="text" name="text" value="<%= memoData.text %>"></p>
        <input type="hidden" name="id" value="<%= memoData.id %>">
        <p><input type="submit" value="更新"></p>
      </form>
    </div>
  </body>
```

delete.ejs(index.ejsとbodyタグ以外は同じ)

```html
  <body>
    <h3><%= title %></h3>
    <p><%= content %></p>
    <div>
      <p>メモ内容：<%= memoData.text %></p>
      <form action="/memo/delete" method="post">
        <input type="hidden" name="id" value="<%= memoData.id %>">
        <p><input type="submit" value="削除"></p>
      </form>
    </div>
  </body>
```

見ての通り基本はHTMLファイルで、値を受け取ってそれを表示したり、値を送ったりしているだけです。
title, content, memoDataという名前で値を送る処理はroutes（サーバー側）から行ってます。



## Step4：CRUD処理を実装
最後に肝心のCRUD処理を実装していきます。

routes配下にmemo.jsというファイルを作り、以下の処理を書いていきます。
各所にコメントを入れてあります。

```javascript
var express = require('express');
var router = express.Router();

var sqlite3 = require('sqlite3');

//データベースオブジェクトの取得
const db = new sqlite3.Database('memo_data.sqlite3');

router.get('/', function(req, res, next) {
    db.serialize(() => {
        //SQL文, memosテーブルから全てのレコードを取得する（* は全て）
        db.all("select * from memos", (err, rows) => {
            if (!err) {
                const data = {
                    title: 'To Do メモ 一覧表示',
                    content: rows //DataBaseから返された全レコードがrowsに配列で入ります
                }
                //viewファイルのmemo/indexにdataオブジェクトが渡されます
                //res.render(テンプレートファイル名, { 渡す値をオブジェクトで }) → テンプレートファイルを描画する
                res.render('memo/index', data);
            }
        })
    })
});

router.get('/add', function(req, res, next) {
    const data = {
        title: '追加',
        content: '新しいデータを入力してください'
    }
    res.render('memo/add', data);
});

router.post('/add', function(req, res, next) {
    const tx = req.body.text;
    //SQL文, DataBaseのレコード作成
    db.run('insert into memos (text) values (?)', tx)
    //res.redirect() 引数に指定したアドレスにリダイレクト
    res.redirect('/memo');
});

router.get('/edit', function(req, res, next) {
    const id = req.query.id;
    db.serialize(() => {
        const q = "select * from memos where id = ?";
        db.get(q, [id], (err, row) => {
            if (!err) {
                const data = {
                    title: '更新',
                    content: 'id = ' + id + 'のレコードを更新',
                    memoData: row
                }
                res.render('memo/edit', data);
            }
        })
    })
});

router.post('/edit', function(req, res, next) {
    //POST送信された値はreq.body内にまとまられている
    const id = req.body.id;
    const tx = req.body.text;
    const q = "update memos set text = ? where id = ?";
    db.run(q, tx, id);
    res.redirect('/memo');
});


router.get('/delete', function(req, res, next) {
    const id = req.query.id;
    db.serialize(() => {
        const q = "select * from memos where id = ?";
        db.get(q, [id], (err, row) => {
            if (!err) {
                const data = {
                    title: '削除',
                    content: 'id = ' + id + 'のメモを削除しますか？',
                    memoData: row
                }
                res.render('memo/delete', data);
            }
        })
    })
});

router.post('/delete', function(req, res, next) {
    const id = req.body.id;
    const q = "delete from memos where id = ?";
    db.run(q, id);
    res.redirect('/memo');
});

module.exports = router;

```

そして、`app.js`に以下を追記してください

```javascript
var memoRouter = require('./routes/memo');

app.use('/memo', memoRouter);
```


ここまでできたら`npm start`を実行して、`http://localhost:3000/memo`を開いてください。
![スクリーンショット 2021-05-03 18.10.57.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/53754208-83e3-2f96-c3f5-a5fa80207480.png)



最初に登録したダミーデータが表示されていれば一覧表示はうまくいってます。
追加、更新、削除も機能していればバッチリです。

![スクリーンショット 2021-05-02 22.09.39.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/16c20446-211c-61f1-99cb-cd0d4ce64cf5.png)

![スクリーンショット 2021-05-02 22.09.21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/88287af6-da2f-1596-84ce-2131b9c9b423.png)

![スクリーンショット 2021-05-02 22.09.30.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/173182/df6e2e9b-bb16-4722-d539-7b5cdc710e89.png)

## なぜサーバーサイドが必要？
ローカルストレージ機能を使えば一時保存できるので、簡易的なメモアプリであればフロントだけで完結できます。しかし、大きすぎるデータや、DataBaseにきちんと保存するのであればサーバーサイドが必要になります。（ブラウザからDataBaseにアクセスできないから）

それ以外の理由ではフロントではできない複雑な処理などをサーバー側でやってもらうと言う理由があります。


## まとめ
テーブル１個のこんなシンプルなアプリなんて現場ではまずあり得ないと思います（せめて複数テーブルの結合innerjoin, outerjoinくらいはやっておくべきでしょうか）が、Node.js、サーバーサイドに徐々に慣れてきました。

次は**REST API**に関しての記事を書こうと思います。