0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Express + Mongoose + EJSで簡易アプリを作る

Posted at

概要

ExpressとMongooseで簡単な商品管理アプリを作成しました。
テンプレートエンジンはEJSを使用しています。

インストール

Express、EJS、Mongooseをインストールしてindex.jsviewsディレクトリを作成します。

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を設定します。

index.js
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を作成し、スキーマとモデルの作成を行ないます。

/models/product.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モデルを呼び出しておきます。

index.js
const Product = require("./models/product")

モックデータ作成(スキップ可)

seeds.js
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でデータの取得とルーティングを行ないます

index.js
app.get("/products", async (req, res) => {
  const products = await Product.find({});
  res.render("products/index", { products });
});

一覧ページのテンプレートを作成します。

/views/products/index.ejs
<ul>
    <% for (let product of products) { %>
        <li>
            <a href='/products/<%= product._id %>'>
                <%= product.name %>
            </a>
        </li>
    <% } %>
</ul>

商品追加ページと追加処理

追加処理
リクエストボディを受け取るために、
app.use(express.urlencoded({ extended: true }));を設定します。

ルーティング
カテゴリーリストを定義して、セレクトボックスに反映できるよう、テンプレートに渡します。

index.js
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`);
});
/views/products/new.ejs
<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から商品を取得。テンプレートにデータを渡します。

index.js
app.get("/products/:id", async (req, res) => {
  const { id } = req.params;
  const product = await Product.findById(id);
  res.render("products/product", { product });
});

詳細ページに編集ページへのリンクを設置しておきます。

/views/products/product.ejs
<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の設定と編集ページのルーティング、更新処理を追加していきます。

index.js
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のクエリを設定します。

/views/products/edit.ejs
<form action='/products/<%= product._id %>?_method=PUT' method="POST">

削除処理

index.js
app.delete("/products/:id", async (req, res) => {
  const { id } = req.params;
  await Product.findByIdAndDelete(id);
  res.redirect("/products");
});

商品詳細ページに削除ボタンを追加します。

/views/products/product.ejs
<form action='/products/<%= product._id %>?_method=DELETE' method="POST">
    <button>削除</button>
</form>

完成

以上、Express+Mongoose+EJSで簡単なCRUD操作が行えるアプリを作成しました。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?