RESTfulなAPIを作成する!(with Node.js Express & MongoDB & TypeScript)
はじめに
本記事では、Node.js ウェッブアプリケーションサーバーフレームワークであるExpress
と、NoSQLのMongoDB
で簡単なAPIを作成する手順をまとめました。
必要なパッケージを導入
yarn add express mongoose
yarn add -D @types/express @types/mongoose @types/node @types/nodemon nodemon ts-node typescript
nodemon,ts-node導入
nodemon
についての概要についてはこちらの記事で勉強させていただきました!
ファイル監視させるためpackage.json
に以下を追記します。
"scripts": {
"start": "nodemon --exec ts-node src/index.ts"
}
ts-node
はソースコードを修正するたびに tsc を実行し、さらにそのあと node コマンドを実行するのは手間なので導入しました。
コードを書く
express
まずはexpress
について軽く触れるため、ポート番号 3000 に起動させます。
import express from "express";
const app = express();
app.get('/',(req:Request,res:Response) => {
res.send('Hello from node');
});
const PORT = process.env.PORT || 3000;// port番号を指定
//サーバー起動
app.listen(PORT, () => console.log(`Server run at port ${PORT}`));
ターミナル上でyarn start
とタイプすると
Server run at port 3000
と表示されれば ok です。
サーバーを起動後Postman
でurlにローカルホスト3000番を指定し、SENDボタンを押すと意図した結果が返されます。
MongoDB
MongoDB
については以下の記事が画像付きでわかりやすいです。
コードに戻り、MongoDB
との接続を図ります。
import express from "express";
+ import mongoose from "mongoose";
const app = express();
app.get('/',(req:Request,res:Response) => {
res.send('Hello from node');
});
+ mongoose
+ .connect(
+ 'mongodb://username:<password>@cluster0-shard-00-00.uppu6.mongodb.net:27017,cluster0-shard-00-01.uppu6.mongodb.net:27017,cluster0-shard-00-02.uppu6.mongodb.net:27017/myFirstDatabase?ssl=true&replicaSet=atlas-k12pxg-shard-0&authSource=admin&retryWrites=true&w=majority',
+ {
+ useNewUrlParser: true, //ユーザーが新しいパーサーにバグを見つけたとき古いパーサーに逆戻りする機能
+ useUnifiedTopology: true,//新しいトポロジエンジンに関連しなくなったいくつかの接続オプションのサポートが削除される機能
+ useFindAndModify: false,
+ }
+ )
+ .then(() => console.log("mongodb connected!"))
+ .catch((error) => console.log(error));
const PORT = process.env.PORT || 3000;// port番号を指定
//サーバー起動
app.listen(PORT, () => console.log(`Server run at port ${PORT}`));
保存すると、nodemon
が働きコンソール上に "mongodb connected!" と表示されるはずです。
ユーザー名やパスワードをリポジトリにコミットさせないためにenvファイル
を導入します。
envファイル
まず必要なパッケージをインストールしましょう。
yarn add dotenv
env ファイルはトップディレクトリに作成します。
├── src/
└──index.tsx
└──.env
└──package.json
└──tsconfig.json
.env内のコードは以下のとおりです。
USERNAME=・・・・・・・・・・・ //MongoDBに登録したユーザー名を指定
PASSWORD=・・・・・・・・・・・ //接続セキュリティ情報を作成時のパスワードをペースト
index.tsファイルは以下のように変更を加えます。
import express from "express";
+ import dotenv from "dotenv";
const app = express();
+ dotenv.config();
app.get('/',(req:Request,res:Response) => {
res.send('Hello from node');
});
mongoose
.connect(
- 'mongodb://~',//MongoDBのurl
+ `mongodb://${process.env.USERNAME}:${process.env.PASSWORD}@cluster0-shard-00-00.96zsr.mongodb.net:27017,cluster0-shard-00-01.96zsr.mongodb.net:27017,cluster0-shard-00-02.96zsr.mongodb.net:27017/myFirstDatabase?ssl=true&replicaSet=atlas-118son-shard-0&authSource=admin&retryWrites=true&w=majority`,
{
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
}
)
.then(() => console.log("mongodb connected!"))
.catch((error) => console.log(error));
const PORT = process.env.PORT || 3000;// port番号を指定
//サーバー起動
app.listen(PORT, () => console.log(`Server run at port ${PORT}`));
Model 作成
モデルを作成するために、MongoDB
のスキーマを利用します。スキーマの役割は、 JSON 形式で変数名と値のペアを定義することです。
ファイル構成を確認しておきましょう。
├── src/
├── models/ ← New
└──User.ts
└──index.tsx
└──.env
└──package.json
└──tsconfig.json
それではコードを書いていきます。
import mongoose, { Schema } from "mongoose";
const UserSchema: Schema = new mongoose.Schema({
username: {
type: String,
require: true,
},
email: {
type: String,
require: true,
},
date: {
type: Date,
default: Date.now,
},
});
export default mongoose.model("Users", UserSchema);
RESTfulなAPIを作成
それでは本題の REST API を作成していきます。
ここではexpress
の Router メソッドを利用します。
ファイル構成は以下のとおりです。
├── src/
├── models/
└──User.ts
├── routes/ ← New
└──api
└──users.ts
└──index.tsx
└──.env
└──package.json
└──tsconfig.json
ルーティングの設定は以下のとおりです。
import express, { Request, Response } from "express";
import Posts from "../../models/Users";
const router = express.Router();
// データを生成(Create)
router.post("/", async (req: Request, res: Response) => {
const newPost = new Posts(req.body);
try {
const post = await newPost.save(); //変数名と値のペアを取得
if (!post) throw Error("error");
res.status(200).json(post); //JSON形式で変数名と値のペア
} catch (error) {
res.status(400).json({ msg: error });
}
});
またindex.tsに変更を加えます。
import express from "express";
import dotenv from "dotenv";
+ import postsRouter from "./routes/api/users";
const app = express();
dotenv.config();
+ app.use(express.json());
app.get('/',(req:Request,res:Response) => {
res.send('Hello from node');
});
mongoose
.connect(
`mongodb://${process.env.USERNAME}:${process.env.PASSWORD}@cluster0-shard-00-00.96zsr.mongodb.net:27017,cluster0-shard-00-01.96zsr.mongodb.net:27017,cluster0-shard-00-02.96zsr.mongodb.net:27017/myFirstDatabase?ssl=true&replicaSet=atlas-118son-shard-0&authSource=admin&retryWrites=true&w=majority`,
{
useNewUrlParser: true,
useUnifiedTopology: true,
useFindAndModify: false,
}
)
.then(() => console.log("mongodb connected!"))
.catch((error) => console.log(error));
+//User routes
+app.use("/api/users", postsRouter); //http://localhost:3000/api/usersにリクエスト
const PORT = process.env.PORT || 3000;// port番号を指定
//サーバー起動
app.listen(PORT, () => console.log(`Server run at port ${PORT}`));
次にpostman
でリクエストを送ります。
画像のように、スキーマ (modules/Users.ts) で定義した変数名と値のペアが JSON 形式で取得できています。
また、MongoDB(Atlas→Collections)
を確認すると、データが取得できていることがわかります。
ただし、スキーマで取得した post の型がany型
です。
これを型付けしていきます。
スキーマの型を定義
型はインターフェ-ス
で定義します。
- import mongoose, { Schema } from "mongoose";
+ import mongoose, { Schema, Document } from "mongoose";
+ export interface IUser extends Document {
+ username: string;
+ email: string;
+ date: Date;
+ }
const UserSchema: Schema = new mongoose.Schema({
username: {
type: String,
require: true,
},
email: {
type: String,
require: true,
},
date: {
type: Date,
default: Date.now,
},
});
- export default mongoose.model("Users", UserSchema);
+ export default mongoose.model<IUser>("Users", UserSchema);//ジェネリックで型を指定
以上の変更により、スキーマで取得した post の型付けができました。
CRUD 実装
最後に CRUD 処理を実装してこの記事を締めようと思います。
import express, { Request, Response } from "express";
import Posts from "../../models/Users";
const router = express.Router();
+// データの読み込み(Read)
+router.get("/", async (req: Request, res: Response) => {
+ try {
+ const posts = await Posts.find();
+ if (!posts) throw Error("error");
+
+ res.status(200).json(posts);
+ } catch (error) {
+ res.status(400).json({ msg: error });
+ }
+});
+// 個々のデータを読み込み(Read)
+router.get("/:id", async (req: Request, res: Response) => {
+ try {
+ const posts = await Posts.findById(req.params.id);
+ if (!posts) throw Error("error");
+
+ res.status(200).json(posts);
+ } catch (error) {
+ res.status(400).json({ msg: error });
+ }
+});
// データを生成(Create)
router.post("/", async (req: Request, res: Response) => {
const newPost = new Posts(req.body);
try {
const post = await newPost.save();
if (!post) throw Error("error");
res.status(200).json(post);
} catch (error) {
res.status(400).json({ msg: error });
}
});
+//データを更新(Update)
+router.patch("/:id", async (req: Request, res: Response) => {
+ try {
+ const post = await Posts.findByIdAndUpdate(req.params.id, req.body);
+ if (!post) throw Error("error");
+
+ res.status(200).json({ success: true });
+ } catch (error) {
+ res.status(400).json({ msg: error });
+ }
+});
+//データを削除(Delete)
+router.delete("/:id", async (req: Request, res: Response) => {
+ try {
+ const post = await Posts.findByIdAndDelete(req.params.id);
+ if (!post) throw Error("error");
+
+ res.status(200).json({ success: true });
+ } catch (error) {
+ res.status(400).json({ msg: error });
+ }
+});
export default router;
findById ~
というメソッドによって id を url に追加するだけで CRUD 処理を実行できます。
操作はシンプルなので、先程作ったデータを削除するだけにとどめておきます。
Postman
に戻り、メソッドをDELETE
に変更します。
送信後、
画像のように、意図した挙動です。
メソッドをGET
に変更し、送信すると
空の配列に変更されており、データを削除することができました。
以上になります。ここまで読んでいただきありがとうございました!!
Discussion