自動化無しに生活無し

WEB開発関係を中心に備忘録をまとめています

Node.jsでCRUD簡易掲示板を作る【RestfulAPI+SQLite】

thumbnail

Node.jsはサーバーサイドで動作するJavaScriptの実行環境である。

Node.jsの強みは非同期処理。DBやファイルIO、リクエスト・レスポンスなどで発生するIOバウンドの処理を高速化できる。

本記事では、APIサーバーとして動作できるよう、基本のCRUDを解説する。

環境構築

mkdir startup_bbs && cd startup_bbs

npm init

npm install express

npm install sqlite3
Copy

基本のHelloworld

index.js

// API
const express = require("express");
const app = express();

app.get("/", (req, res) => {
    const messages = [
        {"message":"helloworld"}
    ]
    res.json(messages);
});

// サーバー起動
app.listen(3000, () => {
    console.log("Server running on http://localhost:3000");
});
Copy

サーバー起動

node index.js
Copy

DBの初期化

db_init.js

const sqlite3 = require("sqlite3").verbose();
const db = new sqlite3.Database("./db.sqlite3");

db.serialize(() => {
    db.run(`
        CREATE TABLE IF NOT EXISTS topic (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            comment TEXT NOT NULL
        )
    `);
    console.log("DB initialized.");
});

db.close();
Copy

次のコマンドで初期化できる

node db_init.js
Copy

index.jsをCRUD簡易掲示板仕様に編集

const express = require("express");
const sqlite3 = require("sqlite3").verbose();

const app = express();
const db = new sqlite3.Database("./db.sqlite3");

app.use(express.json());

// 一覧表示
app.get("/api/v1/topics", (req, res) => {
    db.all("SELECT * FROM topics", (err, rows) => {
        if (err) return res.status(500).json({ error: err.message }); 
        res.json(rows);
    }); 
});

// 個別表示
app.get("/api/v1/topics/:id", (req, res) => {

    db.get("SELECT * FROM topics WHERE id = ?", [req.params.id], (err, row) => {
        if (err) return res.status(500).json({ error: err.message });
        if (!row) return res.status(404).json({ error: "Not found" });
        res.json(row);
    }); 
});

// 登録
app.post("/api/v1/topics", (req, res) => {
    if (!req.body.comment) {
        return res.status(400).json({ error: "comment is required" });
    }

    db.run("INSERT INTO topics (comment) VALUES (?)", [req.body.comment], function(err) { 
        if (err) return res.status(500).json({ error: err.message }); 
        res.json({ id: this.lastID, comment: req.body.comment }); 
    });  
});

// 更新
app.put("/api/v1/topics/:id", (req, res) => {
    if (!req.body.comment) {
        return res.status(400).json({ error: "comment is required" });
    }

    db.run("UPDATE topics SET comment = ? WHERE id = ?", [req.body.comment, req.params.id], function(err) {
        if (err) return res.status(500).json({ error: err.message });
        res.json({ updated: this.changes }); 
    });
});

// 削除
app.delete("/api/v1/topics/:id", (req, res) => {
    db.run("DELETE FROM topics WHERE id = ?", [req.params.id], function(err) {
        if (err) return res.status(500).json({ error: err.message }); 
        res.json({ deleted: this.changes }); 
    }); 
});

app.listen(3000, () => {
    console.log("Server running on http://localhost:3000");
});
Copy

function 関数を使っているのは、thisを使用しているため。

アロー関数を使うとthisを束縛しなくなり外側スコープのthisを参照してしまう。そのため、this.lastIDなどはundefined になる。

サーバーを起動する。

node index.js
Copy

APIクライアント(シェルスクリプト)

#! /bin/bash

# 登録
for i in {1..10}
do
    curl -X POST http://localhost:3000/api/v1/topics \
         -H "Content-Type: application/json" \
         -d '{"comment":"こんにちは世界"}' \
         -w "\n"
done

# 一覧表示
curl -X GET http://localhost:3000/api/v1/topics -w "\n"


# 個別取得
curl -X GET http://localhost:3000/api/v1/topics/1 -w "\n"



# 編集
for i in {4..7}
do
    curl -X PUT http://localhost:3000/api/v1/topics/${i} \
         -H "Content-Type: application/json" \
         -d '{"comment":"このコメントは編集されました"}' \
         -w "\n"
done

# 削除
for i in {1..3}
do
    curl -X DELETE http://localhost:3000/api/v1/topics/${i} \
         -w "\n"
done


# 再度、一覧表示と個別取得
curl -X GET http://localhost:3000/api/v1/topics -w "\n"
curl -X GET http://localhost:3000/api/v1/topics/1 -w "\n"
Copy

見やすく改行をするために、-wオプションを加えた。

動かすとこうなる。

{"id":1,"comment":"こんにちは世界"}
{"id":2,"comment":"こんにちは世界"}
{"id":3,"comment":"こんにちは世界"}
{"id":4,"comment":"こんにちは世界"}
{"id":5,"comment":"こんにちは世界"}
{"id":6,"comment":"こんにちは世界"}
{"id":7,"comment":"こんにちは世界"}
{"id":8,"comment":"こんにちは世界"}
{"id":9,"comment":"こんにちは世界"}
{"id":10,"comment":"こんにちは世界"}
[{"id":1,"comment":"こんにちは世界"},{"id":2,"comment":"こんにちは世界"},{"id":3,"comment":"こんにちは世界"},{"id":4,"comment":"こんにちは世界"},{"id":5,"comment":"こんにちは世界"},{"id":6,"comment":"こんにちは世界"},{"id":7,"comment":"こんにちは世界"},{"id":8,"comment":"こんにちは世界"},{"id":9,"comment":"こんにちは世界"},{"id":10,"comment":"こんにちは世界"}]
{"id":1,"comment":"こんにちは世界"}
{"updated":1}
{"updated":1}
{"updated":1}
{"updated":1}
{"deleted":1}
{"deleted":1}
{"deleted":1}
[{"id":4,"comment":"このコメントは編集されました"},{"id":5,"comment":"このコメントは編集されました"},{"id":6,"comment":"このコメントは編集されました"},{"id":7,"comment":"このコメントは編集されました"},{"id":8,"comment":"こんにちは世界"},{"id":9,"comment":"こんにちは世界"},{"id":10,"comment":"こんにちは世界"}]
{"error":"Not found"}
Copy

先の this.changes は 編集・削除をした行数を意味している。

補足事項

【補足1】Nodeはデフォルトでファイルの編集を検知して自動的にサーバーを再起動はしない

そのため、nodemonを使用する

npm install 
Copy
スポンサーリンク