🐙

Express.jsを使ってtodo appを作る。

2022/12/06に公開約10,100字

Express.jsフレームワークを使ってTodo Appを作ってみました。
用語の間違いは指摘していただけると幸いです。
よろしくお願いいたします。

使用環境
Arch: x86_64
Docker: 20.10.21
Docker Compose version 2.13.0
node 18.12.1
mongo 4.4.6

Express.jsとは

ExpressとはNodejsで利用できるWebアプリケーションフレームワークです。
高速、革新的、最小限なフレームワーク(公式HPより)
Express.jsを利用することでシングルページ、マルチページ、およびハイブリッドWebアプリケーションを構築することができる。
フレームワークは他にもKoa.js, Nest.js, Meteor-jsなどがあります。
最小限&最軽量の構成なので、ORM、テンプレートエンジンも何もない状態から始め、開発者が必要に応じて、パッケージのインストールやカスタマイズしていくので拡張性が高い
現在はgithubスター数59.1k

  • コア機能
    • 単一ページ、複数ページ、およびハイブリッド Web アプリケーションの設計
    • HTTP リクエストに応答するミドルウェアをセットアップ
    • HTTP メソッドと URL に基づいてさまざまなアクションを実行するために使用されるルーティングテーブルを定義
    • テンプレートエンジンを使用し、それに引数を渡すことによって動的レンダリングを行う

express-geenrator

create-react-appのようにアプリケーションスケルトンを作成するツール

bash
npm  i -g express-generator # グローバルインストールする
express --view=使うview名(ejs, pugなど) app名

まずHello Worldを表示

/にアクセスしたときHell Worldを表示する

req

第一引数で指定されたパスに入ってきたHTTPリクエストを表すオブジェクト
POSTメソッドによるフォームからの入力はreq.body.name名になる

res

指定されたパスに入ってきたリクエストに対するHTTPレスポンスを構成するためのオブジェクト
res.send('Hello World');とすることでHello Worldというレスポンスを返す

import express from 'express';

const app = express();

app.get('/', (req, res) => {
  res.send('Hello World');
})

app.listen(3000);

今回はテンプレートエンジンを使わずに作ってみました。
まず使うモジュールをインストールします。

bash
npm init -y
npm i express mongoose method-override nodemon

package.jsonがプロジェクトのルートディレクトリにあるので下記のようにした後、index.jsを作成する
nodemonを使いプログラムをdemon化する
ファイルが更新されると自動で再起動する。

package.json
"scripts": {
    "start": "nodemon index.js",	
}

index.jsに追加

index.js
import express from 'express' // expressのインポート

const app = exporess();
const mongoose = require('mongoose'); // mongodbの操作に使用
const methodOverride = require('method-override'); // フォームの入力(PUT, Deleteリクエストの発行に)とかに必要
const Schema = mongoose.Schema; // このSchemaは学習不足で理解が足りない

app.use('methodOverride('_method')'); // ミドルウェアの登録、postとgetリクエストしか通常ないためput, deleteを使えるようにするために拡張機能
app.use('express.urlencoded({extended: true}');

// スキーマの作成 typscriptの型定義に似ていると思った
let todoType = new Schema({
    _id: Number,
    todo: String,
});

// mongodbに接続
mongoose.connect("mongodb://username:password@mongoのservice名:port番号/database名?authSource=admin") // ご自身のdockerで建てたmongooseのurlを

const Todo = mongoose.model('collection名', todoType); // Todoというモデルを作成

// ここにコードを追加します

app.listen(3000);

1: 全データの取得
for文ですべてのデータのaタグを作りそれを表示する

app.get('/', async(req, res) => {
	// thenを使ってデータが見つかったときの処理を書く
	await Todo.find().then(docs => { // model.find()空にすることですべてを取得できるもしくはfind({})
		let data = '';
		let(for i = 0; i < docs.length; i++){
			data += `a href=edit/${docs[i]._id}>${documents[i]}</a>` // リンクで各リストの詳細に飛べるようにする
		}
		res.send(`
			<a href="/create">Create</a>
			${data} // 一覧を表示
		`);
	}).catch(e => {throw e})
})

全データを表示した写真↓
全データを表示した写真

2: 新規Todoリストを作成するフォームを表示

index.js
app.get('/create', (req, res) => {
  // res.sendFile(__dirname + '/public/create.html'); // htmlファイルを表示する場合この表記
  res.send(`
    <form action="/create" method="post"> 
      <input type="text" name="post" id="post">
      <button type="submit">submit</button>
    </form>
  `)
});

app.post('/create', async(req, res) => {
  await Todo.find({}).sort({ _id: -1 }).limit(1).then(products => { // 最後のドキュメントのidを取得しid+1でデータを追加している
    const newData = new Todo({
      _id: products[0]._id + 1,
      todo: req.body.post // フォームの値
    });   
    newData.save().then(() => {
      res.redirect('/'); // saveしたあとトップページにリダイレクト
    });
  });
});

新しいアイテム作成用フォームの写真↓
新しいアイテム作成用フォームの写真

3: アイテム編集用フォームの表示

index.js
app.get('/edit/:id', async(req, res) => { // URIは/edit/1, 2などのtodoリストのidが入ります。
  let data = await Todo.findOne({_id: req.params.id}); // req.params.idはURIの1,2が入り、idと合致するアイテムを取得する。
  res.send(`
    <h1>Edit</h1>
    ${data}
    <form action="/edit/${req.params.id}?_method=PUT" method="POST"> // _method=PUTと指定しアイテムを編集できるようにする
      <input type="text" name="edit" id="edit">
      <button type="submit">Update</button>
    </form>
    <form action="/delete/${req.params.id}?_method=Delete" method="POST"> // _method=Deleteと指定しアイテムを削除できるようにする
      <button type="submit">Delete</button>
    </form>
    <a href="/">Back</a>
  `);
});

app.put('/edit/:id', async(req: express.Request, res: express.Response) => {
  await Todo.updateOne({_id: req.params.id}, {todo: req.body.edit}); // idが一致するアイテムを編集
  res.redirect('/'); // トップページにリダイレクト
});

アイテム編集フォームの写真↓
アイテム編集フォームの写真
アイテム編集後の写真↓
アイテム編集後の写真

4: リストの削除用のルート
editページから削除するためgetリクエストはいりません

index.js
app.delete('/delete/:id', async(req, res) => {
  await Todo.deleteOne({_id: req.params.id}); // idが:idと一致するアイテムを削除する
  res.redirect('/'); // トップページにリダイレクト
});

deleteした後の写真↓
deleteした後の写真

今回の反省点は公式ドキュメントから調べずに個人技術ブログ,stackoverflowなどから調べ始めたことで、情報が正しいか正しくないかが判別が難しかったこと。
調べる順番を間違ったことで、この簡単なプログラムに2日もかかってしまった。
なのでこれからはまず公式ドキュメントから調べ補足として個人技術ブログ,stackoverflowを使っていこうと思う。
またHTTPリクエスト、レスポンスやExpress、Nodejsについてももっと理解を深めていこうと思う。

参考にしたサイト一覧

GitHubで編集を提案

Discussion

ログインするとコメントできます