Express.jsでTODOアプリケーションの作成を作成する方法
概要
Express v4.17.1
nodejs v19.7.0
前半ではExpress.jsを使用してAPIを構築し、後半ではHTMLとJavaScriptを使用してフロントエンドを作成することで、完全なTODOアプリケーションを作成します。
Express.js v4.17.1
Node.js v16.3.0
また、今回作成するコードは全て、GitHubに掲載しています。
APIのエンドポイント:
GET /todos すべての TODO を取得する
POST /todos 新しい TODO を作成する
PUT /todos/:id 既存の TODO を更新する
DELETE /todos/:id 既存の TODO を削除する
フロントエンドの機能:
TODOのリストを表示する
新しいTODOを作成する
既存のTODOを更新する
既存のTODOを削除する
TODOアプリのバックエンドを作成する方法
server.js
// モジュールをインポートします
const express = require("express");
const app = express();
const path = require('path');
// HTMLファイルをホストするディレクトリを指定します。
app.use(express.static(path.join(__dirname, 'public')));
// 一覧用の Todo を作成します
let todos = [
{ id: 1, text: "野球をする", completed: false },
{ id: 2, text: "ゲームをする", completed: false },
{ id: 3, text: "仕事をする", completed: false },
];
app.use(express.json());
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
// '/todos'パスで Todo の一覧を取得します
app.get("/todos", (req, res) => {
res.json({ todos });
});
// "/todos"パスに POST リクエストを送信して Todo を新しく作成します
app.post("/todos", (req, res) => {
const newTodo = req.body;
newTodo.id = todos.length + 1;
todos.push(newTodo);
res.json({ todo: newTodo });
});
// "/todos/:id"パスに PUT リクエストを送信して Todo を更新します
app.put("/todos/:id", (req, res) => {
const id = req.params.id;
let todo = todos.find((t) => t.id == id);
if (todo) {
Object.assign(todo, req.body);
res.json({ todo });
} else {
res.status(404).send({ message: "Todo が見つかりません" });
}
});
// "/todos/:id"パスに DELETE リクエストを送信して Todo を削除します
app.delete("/todos/:id", (req, res) => {
const id = req.params.id;
const index = todos.findIndex((t) => t.id == id);
if (index !== -1) {
todos.splice(index, 1);
res.json({ message: "Todo が削除されました" });
} else {
res.status(404).send({ message: "Todo が見つかりません" });
}
});
// ポート 3000 番でサーバーをリッスンします
app.listen(3000, () => {
console.log(`サーバーがポート3000でリッスン中です`);
});
解説
// モジュールをインポートします
const express = require("express");
const app = express();
const path = require('path');
上記のコードではExpressというモジュールとpathというモジュールをインポートしています。express()
関数を呼び出して作成されたapp
オブジェクトは、HTTPリクエストを処理するためのメソッドを含みます。
// HTMLファイルをホストするディレクトリを指定します。
app.use(express.static(path.join(__dirname, 'public')));
ここでは、静的なファイル(HTML、CSS、JavaScript、画像ファイルなど)を配信するためのミドルウェアを設定しています。このミドルウェアは、指定されたディレクトリ内のファイルをHTTP経由で直接提供します。
app.use(express.json());
これはミドルウェアの一部で、これを設定することで、サーバーはJSON形式のボディを持つPOSTリクエストを解析できます。
app.get('/', (req, res) => {
res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
このコードでは、ルートパス(/
)へのGETリクエストに対する応答を定義しています。応答として、サーバーはpublic
ディレクトリ内のindex.html
ファイルを返します。
app.get("/todos", (req, res) => {
res.json({ todos });
});
この部分では、/todos
パスに対するGETリクエストの処理を設定しています。これにより、このパスへのリクエストがあったときにはtodos
配列をJSON形式で返します。
app.post("/todos", (req, res) => {
const newTodo = req.body;
newTodo.id = todos.length + 1;
todos.push(newTodo);
res.json({ todo: newTodo });
});
この部分では、/todos
パスに対するPOSTリクエストを処理しています。新しいTODO項目を作成し、それをtodos
配列に追加します。そして、新しいTODO項目をクライアントにJSON形式で返します。
app.put("/todos/:id", (req, res) => {
const id = req.params.id;
let todo = todos.find((t) => t.id == id);
if (todo) {
Object.assign(todo, req.body);
res.json({ todo });
} else {
res.status
(404).send({ message: "Todo が見つかりません" });
}
});
この部分では、/todos/:id
パスに対するPUTリクエストを処理しています。指定されたIDを持つTODO項目を探し、それをリクエストのボディで更新します。そして、更新されたTODO項目をクライアントにJSON形式で返します。
app.delete("/todos/:id", (req, res) => {
const id = req.params.id;
const index = todos.findIndex((t) => t.id == id);
if (index !== -1) {
todos.splice(index, 1);
res.json({ message: "Todo が削除されました" });
} else {
res.status(404).send({ message: "Todo が見つかりません" });
}
});
この部分では、/todos/:id
パスに対するDELETEリクエストを処理しています。指定されたIDを持つTODO項目を探し、それをtodos
配列から削除します。そして、クライアントに対して削除が成功したことを示すメッセージをJSON形式で返します。
// ポート 3000 番でサーバーをリッスンします
app.listen(3000, () => {
console.log(`サーバーがポート3000でリッスン中です`);
});
最後に、サーバーはポート3000でリッスンします。これにより、サーバーはポート3000で受信した全てのHTTPリクエストを処理します。
TODOアプリのフロントエンドを作成する方法
次に、フロントエンド部分を作成します。以下のHTMLとJavaScriptをindex.html
という名前のファイルに保存します。
<!DOCTYPE html>
<html>
<head>
<title>TODOアプリ</title>
<style>
/* ここにCSSを書く */
</style>
</head>
<body>
<h1>TODOアプリ</h1>
<input id="todo-input" type="text" placeholder="新しいTODOを入力" />
<button id="add-todo">追加</button>
<ul id="todo-list"></ul>
<template id="todo-item-template">
<li>
<span class="todo-text"></span>
<button class="delete-todo">削除</button>
<button class="update-todo">完了</button>
</li>
</template>
<script>
const todoInput = document.querySelector("#todo-input");
const addTodoButton = document.querySelector("#add-todo");
const todoList = document.querySelector("#todo-list");
const todoItemTemplate = document.querySelector("#todo-item-template");
// すべてのTODOを取得し、画面に表示する
function fetchTodos() {
fetch("http://localhost:3000/todos")
.then((response) => response.json())
.then((data) => {
todoList.innerHTML = "";
data.todos.forEach((todo) => {
const todoItem = todoItemTemplate.content.cloneNode(true);
const textElement = todoItem.querySelector(".todo-text");
const deleteButton = todoItem.querySelector(".delete-todo");
const updateButton = todoItem.querySelector(".update-todo");
textElement.textContent = todo.text;
updateButton.textContent = todo.completed ? "未完了" : "完了";
deleteButton.addEventListener("click", (event) => {
event.stopPropagation();
deleteTodo(todo);
});
updateButton.addEventListener("click", (event) => {
event.stopPropagation();
updateTodo(todo);
});
todoList.appendChild(todoItem);
});
});
}
// 新しいTODOを作成する
function addTodo() {
const newTodo = { text: todoInput.value, completed: false };
fetch("http://localhost:3000/todos", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(newTodo),
}).then(() => {
fetchTodos();
todoInput.value = "";
});
}
// TODOを更新する
function updateTodo(todo) {
todo.completed = !todo.completed;
fetch(`http://localhost:3000/todos/${todo.id}`, {
method: "PUT",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify(todo),
}).then(fetchTodos);
}
// TODOを削除する
function deleteTodo(todo) {
fetch(`http://localhost:3000/todos/${todo.id}`, {
method: "DELETE",
}).then(fetchTodos);
}
addTodoButton.addEventListener("click", addTodo);
fetchTodos();
</script>
</body>
</html>
解説
HTML部分には <template>
タグを使用して、TODOアイテムのレイアウトを定義します。
これは非表示の要素で、その内容をJavaScriptで複製して表示するために使用します。
<template id="todo-item-template">
<li>
<span class="todo-text"></span>
<button class="delete-todo">削除</button>
<button class="update-todo">完了</button>
</li>
</template>
このテンプレート内の要素は、JavaScriptから参照できるようにクラス名を割り当てています。
const todoItem = todoItemTemplate.content.cloneNode(true);
const textElement = todoItem.querySelector(".todo-text");
const deleteButton = todoItem.querySelector(".delete-todo");
const updateButton = todoItem.querySelector(".update-todo");
新たなTODOアイテムを生成するために、テンプレートの内容を複製し(.content.cloneNode(true)
)、テキストとボタンをカスタマイズします。ボタンそれぞれには、対応する操作(削除や更新)を行うイベントリスナーを追加しています。
終わりに
この記事では、Express.jsを使用してバックエンドのAPIを作成し、そのAPIを使用してHTMLとJavaScriptでフロントエンドのTODOアプリを作成する方法を紹介しました。ここでは非常に基本的なアプリケーションを作成しましたが、これを基にしてさらに複雑なアプリケーションを作成することも可能です。