概要
ExpressとMongooseで簡単な商品管理アプリを作成しました。
テンプレートエンジンはEJSを使用しています。
インストール
Express、EJS、Mongooseをインストールしてindex.js
とviews
ディレクトリを作成します。
npm i express ejs mongoose
touch index.js
mkdir views
作成時のパッケージのバージョンは以下です。
ejs: "^3.1.10"
express: "^4.19.2"
mongoose: "^8.3.3"
次にExpress、EJS、Mongooseを設定します。
const express = require("express");
const app = express();
const path = require("path");
// Mongooseの設定
const mongoose = require("mongoose");
main().catch((err) => console.log(err));
async function main() {
await mongoose.connect("mongodb://127.0.0.1:27017/shopApp");
console.log("db connected");
}
app.set("views", path.join(__dirname), "views");
app.set("view engine", "ejs");
app.listen(3000, () => {
console.log("watching...");
});
nodemonを使ってサーバーを起動します。
nodemon index.js
modelの作成
models
ディレクトリにproject.js
を作成し、スキーマとモデルの作成を行ないます。
const mongoose = require("mongoose");
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true,
},
price: {
type: Number,
required: true,
min: 0,
},
category: {
type: String,
enum: ["食品・飲料", "家電", "おもちゃ"],
},
});
const Product = mongoose.model("Product", productSchema);
module.exports = Product;
index.jsからProductモデルを呼び出しておきます。
const Product = require("./models/product")
モックデータ作成(スキップ可)
const mongoose = require("mongoose");
const Product = require("./models/product");
main().catch((err) => console.log(err));
async function main() {
await mongoose.connect("mongodb://127.0.0.1:27017/productApp");
console.log("db connected");
}
// 商品を1つずつ追加
const p = new Product({
name: "お茶",
price: 160,
category: "食品・飲料",
});
p.save()
// またはまとめて追加
const products = [
{
name: "エアコン",
price: 29800,
category: "家電",
},
{
name: "ポケモン",
price: 3980,
category: "おもちゃ",
}
...
];
Product.insertMany(products)
nodeでseeds.jsを実行するとサンプルデータがインサートされます。
node seeds.js
一覧ページの作成
index.jsでデータの取得とルーティングを行ないます
app.get("/products", async (req, res) => {
const products = await Product.find({});
res.render("products/index", { products });
});
一覧ページのテンプレートを作成します。
<ul>
<% for (let product of products) { %>
<li>
<a href='/products/<%= product._id %>'>
<%= product.name %>
</a>
</li>
<% } %>
</ul>
商品追加ページと追加処理
追加処理
リクエストボディを受け取るために、
app.use(express.urlencoded({ extended: true }));
を設定します。
ルーティング
カテゴリーリストを定義して、セレクトボックスに反映できるよう、テンプレートに渡します。
app.use(express.urlencoded({ extended: true }));
const categories = ["食品・飲料", "家電", "おもちゃ"];
// 商品追加ページ
app.get("/products/new", (req, res) => {
res.render("products/new", { categories });
});
// 商品追加処理
app.post("/products", async (req, res) => {
const newProduct = new Product(req.body);
await newProduct.save();
res.redirect(`/products`);
});
<form action='/products' method="POST">
<label for='name'>商品名</label>
<input type='text' name="name" id="name" placeholder="商品名">
<label for='price'>価格</label>
<input type='number' name='price' id="price" placeholder="価格">
<label for='category'>カテゴリー</label>
<select name='category' id="category">
<% for(let category of categories){ %>
<option value='<%= category %>'>
<%= category %>
</option>
<% } %>
</select>
<button>登録</button>
</form>
詳細ページの作成
findById()
を使用してparamsから商品を取得。テンプレートにデータを渡します。
app.get("/products/:id", async (req, res) => {
const { id } = req.params;
const product = await Product.findById(id);
res.render("products/product", { product });
});
詳細ページに編集ページへのリンクを設置しておきます。
<h1>
<%= product.name %>
</h1>
<ul>
<li>価格:<%= product.price %>円</li>
<li>カテゴリー:<%= product.category %></li>
</ul>
<a href='/products'>商品一覧</a>
<a href='/products/<%= product._id %>/edit'>編集</a>
商品編集ページと更新処理
更新処理にPUTを使用するため、method-override
をインストールします。
npm i method-override
method-override
の設定と編集ページのルーティング、更新処理を追加していきます。
const methodOverride = require("method-override");
app.use(methodOverride("_method"));
// 商品編集ページ
app.get("/products/:id/edit", async (req, res) => {
const { id } = req.params;
const product = await Product.findById(id);
res.render("products/edit", { product, categories });
});
// 商品更新処理
app.put("/products/:id", async (req, res) => {
const { id } = req.params;
const product = await Product.findByIdAndUpdate(id, req.body, {
runValidators: true, // バリデーションを有効にする
new: true, // newオプションを付与することで更新後のオブジェクトが返される
});
res.redirect(`/products/${product._id}`);
});
補足
runValidators: true
を設定することで、スキーマのrequired
,min
等のバリデーションが有効になります。new: true
を設定しないと、更新前のオブジェクトが返ってくるので注意。
編集ページのformのactionに下記のように?_method=PUT
のクエリを設定します。
<form action='/products/<%= product._id %>?_method=PUT' method="POST">
削除処理
app.delete("/products/:id", async (req, res) => {
const { id } = req.params;
await Product.findByIdAndDelete(id);
res.redirect("/products");
});
商品詳細ページに削除ボタンを追加します。
<form action='/products/<%= product._id %>?_method=DELETE' method="POST">
<button>削除</button>
</form>
完成
以上、Express+Mongoose+EJSで簡単なCRUD操作が行えるアプリを作成しました。