mongoDBとexpress(node.jsのフレームワーク)で、簡単なrestful APIサーバを作ってみた。
mongodb入門。windowsにサーバとmongosh(CLIクライント)をインストール。CRUD操作をしてみる。SQL文と違って、コマンドというか関数っぽい感じなのね。
1, nodeプロジェクトを作る
1 2 3 4 5 6 7 8 |
mkdir api cd api npm init -y # mongoose = mongodbのORマッパー(テーブル定義) # cors = 別オリジンからでもコールOK # dotent = .envでDB接続情報など npm install express mongoose cors dotenv |
2, apiフォルダ内に.envファイルを作成して、DB接続情報を書き込む
1 |
DATABASE_URL=mongodb://localhost:27017 |
3, apiサーバファイル(server.js)を生成
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
const express = require('express'); // Expressモジュールのインポート const mongoose = require('mongoose'); // Mongooseモジュールのインポート const cors = require('cors'); // CORSモジュールのインポート require('dotenv').config(); // 環境変数の読み込み const app = express(); // Expressアプリケーションのインスタンスを作成 app.use(cors()); // CORSミドルウェアを適用 app.use(express.json()); // JSONのボディパーサミドルウェアを適用 // データベースに接続 mongoose.connect(process.env.DATABASE_URL) .then(() => console.log('MongoDBに接続されました')) // 接続成功時のログ .catch(err => console.log(err)); // 接続失敗時のエラーハンドリング // ポート設定とサーバーの起動 const PORT = process.env.PORT || 5000; app.listen(PORT, () => console.log(`サーバーがポート${PORT}で実行中です`)); // アイテムルートのインポート const itemsRouter = require('./routes/items'); // アイテムルートをアプリケーションに適用 app.use('/items', itemsRouter); // 未定義ルートへのアクセスに対するエラーハンドリング app.use((req, res, next) => { res.status(404).send("申し訳ありませんが、お探しのページは見つかりませんでした。"); }); // サーバーエラーのハンドリング app.use((err, req, res, next) => { console.error(err.stack); // エラー内容をログに出力 res.status(500).send('サーバーエラーが発生しました!'); // 500エラーをクライアントに送信 }); |
4, mongooseを使って、スキーマ定義(テーブル定義)
必須項目も設定できる
models/Item.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
const mongoose = require('mongoose'); // mongooseライブラリをインポート const Schema = mongoose.Schema; // mongooseのSchemaコンストラクタを取得 // itemSchemaを定義 const itemSchema = new Schema({ name: { type: String, required: true }, // 商品名、必須フィールドとしてString型を指定 quantity: { type: Number, required: true }, // 数量、必須フィールドとしてNumber型を指定 description: String // 説明、必須でないString型フィールド }); // 'Item'という名前でitemSchemaに基づいたモデルを作成 const Item = mongoose.model('Item', itemSchema); // このモデルを他のファイルで使用できるようにエクスポート module.exports = Item; |
5, APIエンドポイントの作成(CRUDルーター処理)
routes/items.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
const express = require('express'); const router = express.Router(); const Item = require('../models/Item'); // 全てのアイテムを取得 router.get('/', async (req, res) => { try { const items = await Item.find(); res.json(items); } catch (err) { res.status(500).json({ message: err.message }); } }); // 単一のアイテムをIDで取得 router.get('/:id', async (req, res) => { try { const item = await Item.findById(req.params.id); if (!item) { return res.status(404).json({ message: "アイテムが見つかりません" }); } res.json(item); } catch (err) { res.status(500).json({ message: err.message }); } }); // 新しいアイテムを追加 router.post('/', async (req, res) => { const item = new Item({ name: req.body.name, quantity: req.body.quantity, description: req.body.description }); try { const newItem = await item.save(); res.status(201).json(newItem); } catch (err) { res.status(400).json({ message: err.message }); } }); // アイテムを更新 router.patch('/:id', async (req, res) => { try { const item = await Item.findById(req.params.id); if (!item) { return res.status(404).json({ message: "アイテムが見つかりません" }); } item.name = req.body.name || item.name; item.quantity = req.body.quantity || item.quantity; item.description = req.body.description || item.description; const updatedItem = await item.save(); res.json(updatedItem); } catch (err) { res.status(400).json({ message: err.message }); } }); // アイテムを削除 router.delete('/:id', async (req, res) => { try { const item = await Item.findByIdAndDelete(req.params.id); if (!item) { return res.status(404).send('Item not found'); } res.send('Item deleted'); } catch (err) { res.status(500).send('Server error'); } }); module.exports = router; |
6, seed.js(サンプル用に3件INSERTするコード)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
const mongoose = require('mongoose'); const Item = require('./models/Item'); // アイテムモデルをインポート require('dotenv').config(); // データベース接続 mongoose.connect(process.env.DATABASE_URL) .then(() => console.log('MongoDBに接続されました')) .catch(err => console.log(err)); // サンプルデータ const items = [ { name: 'ノートパソコン', quantity: 10, description: '高性能なノートPC' }, { name: 'デスクトップパソコン', quantity: 5, description: 'ゲーミングPC' }, { name: 'タブレット', quantity: 20, description: '携帯便利なタブレット' } ]; // データ挿入関数 const seedDB = async () => { await Item.deleteMany({}); // 既存のデータをクリア await Item.insertMany(items); // サンプルデータを挿入 console.log('データベースにサンプルデータが挿入されました'); }; // シードプロセスを実行し、完了後にデータベース接続を切断 seedDB().then(() => { mongoose.connection.close(); }); |
7, seeding & APIサーバ起動
1 2 3 4 5 |
# 3件追加 node seed.js # APIサーバ起動(mongoDB接続状態) node server.js |
8, CURLでCRUD操作してみる
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
# 全件表示 curl -X GET http://localhost:5000/items [ { "_id": "662b430371555e4f953d39b7", "name": "ノートパソコン", "quantity": 10, "description": "高性能なノートPC", "__v": 0 }, { "_id": "662b430371555e4f953d39b8", "name": "デスクトップパソコン", "quantity": 5, "description": "ゲーミングPC", "__v": 0 }, { "_id": "662b430371555e4f953d39b9", "name": "タブレット", "quantity": 20, "description": "携帯便利なタブレット", "__v": 0 } ] # 個別詳細 curl -X GET http://localhost:5000/items/662b430371555e4f953d39b7 { "_id": "662b430371555e4f953d39b7", "name": "ノートパソコン", "quantity": 10, "description": "高性能なノートPC", "__v": 0 } # INSERT curl -X POST http://localhost:5000/items -H "Content-Type: application/json" -d "{\"name\":\"test_device2\", \"quantity\":123}" { "name": "test_device2", "quantity": 123, "_id": "662b433489706509d97c9492", "__v": 0 } # UPDATE curl -X PATCH http://localhost:5000/items/662b433489706509d97c9492 -H "Content-Type: application/json" -d "{\"name\":\"test_device3\", \"quantity\":456}" { "_id": "662b433489706509d97c9492", "name": "test_device3", "quantity": 456, "__v": 0 } # DELETE curl -X DELETE http://localhost:5000/items/662b433489706509d97c9492 Item deleted curl -X DELETE http://localhost:5000/items/662b433489706509d97c9492 Item not found |
9, reactでAPIサーバのフロントエンドを作る
1 2 3 4 |
npx create-react-app items-frontend cd items-frontend # 非同期通信axiosパッケージ導入 npm install axios |
App.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
import React, { useState, useEffect } from 'react'; import axios from 'axios'; function App() { const [items, setItems] = useState([]); const [formData, setFormData] = useState({ name: '', quantity: '', description: '' }); useEffect(() => { fetchItems(); }, []); const fetchItems = async () => { try { const response = await axios.get('http://localhost:5000/items'); setItems(response.data); } catch (error) { console.error('Error fetching items:', error); } }; const handleFormChange = event => { const { name, value } = event.target; setFormData(prevState => ({ ...prevState, [name]: value })); }; const handleSubmit = async event => { event.preventDefault(); try { await axios.post('http://localhost:5000/items', formData); setFormData({ name: '', quantity: '', description: '' }); fetchItems(); // Refresh items list } catch (error) { console.error('Error adding item:', error); } }; const handleDelete = async id => { try { await axios.delete(`http://localhost:5000/items/${id}`); fetchItems(); // Refresh items list } catch (error) { console.error('Error deleting item:', error); } }; const handleUpdate = async (id, data) => { try { await axios.put(`http://localhost:5000/items/${id}`, data); fetchItems(); // Refresh items list } catch (error) { console.error('Error updating item:', error); } }; return ( <div> <h1>Items</h1> <form onSubmit={handleSubmit}> <input type="text" name="name" value={formData.name} onChange={handleFormChange} placeholder="Name" /> <input type="number" name="quantity" value={formData.quantity} onChange={handleFormChange} placeholder="Quantity" /> <textarea name="description" value={formData.description} onChange={handleFormChange} placeholder="Description" /> <button type="submit">Add Item</button> </form> <ul> {items.map(item => ( <li key={item._id}> {item.name} - {item.quantity} - {item.description} <button onClick={() => handleDelete(item._id)}>Delete</button> <button onClick={() => handleUpdate(item._id, { ...item, quantity: item.quantity + 1 })}>Update Quantity</button> </li> ))} </ul> </div> ); } export default App; |
1 |
npm start |