node.js + express + sqlite3でWebAPIを作るニーズがあったのでメモ。
準備
場所の作成と必要モジュールインストール。実装ファイル生成。
cd
mkdir node-api-test
cd node-api-test
npm init -y
npm install express
npm install sqlite3
touch index.js
実装
淡々と実装。
知っておいた方がいいこと
sqlite3ではinsertしたりupdateしたIDをthis.lastIDやthis.changesで取得することができるが、this.*という構文はラムダ関数の中では利用できません。幸いなことに?const stmt = db.prepare();を利用すると、stmt.lastID, stmt.changesで取得できるようになるので、冗長とはなりますが、stmt.prepare()を利用しています。
最適な書き方は引き続き模索中。今後変えるかも。
const express = require("express");
const app = express();
//POSTできたりするように(おまじない)
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
//sqlite3関連設定
const sqlite3 = require("sqlite3");
const db = new sqlite3.Database("./test.db", (err) => {
if (err) {
console.error("database error: " + err.message);
} else {
db.serialize(() => {
//都度table削除(あれば)
db.run("drop table if exists members");
//table生成(無ければ)
db.run("create table if not exists members( \
id integer primary key autoincrement, \
name nverchar(32), \
age integer \
)", (err) => {
if (err) {
console.error("table error: " + err.message);
} else {
//初期データinsert
db.run("insert into members(name,age) values(?,?)", "hoge", 11);
db.run("insert into members(name,age) values(?,?)", "foo", 22);
db.run("insert into members(name,age) values(?,?)", "bar", 33);
}
});
});
}
});
//リッスン開始
app.listen(3000, () => {
console.log("Start server on port 3000.");
});
app.get("/", (req, res) => {
res.send("welcome");
});
//create
app.post("/members", (req, res) => {
const reqBody = req.body;
const stmt = db.prepare("insert into members(name,age) values(?,?)"); //lastID取得のため
stmt.run(reqBody.name, reqBody.age, (err, result) => { //lambda式を使うとthis.lastIDでは取得できない
if (err) {
res.status(400).json({
"status": "error",
"message": err.message
});
return;
} else {
res.status(201).json({
"status": "OK",
"lastID": stmt.lastID
});
}
});
});
//get members
app.get("/members", (req, res) => {
db.all("select * from members", [], (err, rows) => {
if (err) {
res.status(400).json({
"status": "error",
"message": err.message
});
return;
} else {
res.status(200).json({
"status": "OK",
"members": rows
});
}
});
});
//get member
app.get("/members/:id", (req, res) => {
const id = req.params.id;
db.get("select * from members where id = ?", id, (err, row) => {
if (err) {
res.status(400).json({
"status": "error",
"message": err.message
});
return;
} else {
res.status(200).json({
"status": "OK",
"members": row
});
}
})
})
//update member
app.patch("/members", (req, res) => {
const reqBody = req.body;
const stmt = db.prepare("update members set name = ?, age = ? where id = ?");
stmt.run(reqBody.name, reqBody.age, reqBody.id, (err, result) => {
if (err) {
res.status(400).json({
"status": "error",
"message": err.message
});
return;
} else {
res.status(200).json({
"status": "OK",
"updatedID": stmt.changes
});
}
})
})
//delete member
app.delete("/members/:id", (req, res) => {
const id = req.params.id;
const stmt = db.prepare("delete from members where id = ?");
stmt.run(id, (err, result) => {
if (err) {
res.status(400).json({
"status": "error",
"message": err.message
});
return;
} else {
res.status(200).json({
"status": "OK",
"deletedID": stmt.changes
});
}
})
})
動作確認
では動作確認をしていきます。APIのテストにはPastmanやVSCodeのプラグインが利用できますが、ここではcurlを利用します。
実行
いつもの。
node index.js
CREATE
挿入。
curl -s -X POST -H "Content-Type: application/json" -d '{"name":"user1","age":"44"}' http://localhost:3000/members
READ(ALL/単体)
取得は全件と単体。全件に関しては実務ではページネーション機能の実装が必要でしょう。
全てを取得
curl -s -X GET http://localhost:3000/members
IDを指定して取得
curl -s -X GET http://localhost:3000/members/1
UPDATE
URLパラメータでIDを指定する方法もあるかと思いますが、ここではmemberオブジェクト全部を投げる。
curl -s -X PATCH -H "Content-Type: application/json" -d '{"id":"1","name":"hoge","age":"99"}' http://localhost:3000/members
DELETE
削除。
curl -s -X DELETE http://localhost:3000/members/1
存在しないIDを投げるとエラーではなく、changesとして0が戻る。
本番環境では
下記が最低限必要です(まあ、そもそもsqlite3を本番で使うことないですが)
- CORS対応
- 受け取った値のバリデーション