ExpressとmongoDBを使ったREST APIをTypeScriptで組んでみる【3.CRUD操作編】
はじめに
前回作成したPostモデルを操作するためのコントローラーを作成します。
まず/posts
にアクセスして操作ができるようにルーティングから設定します。
Routeの設定
/posts/
でget、POSTメソッドを利用できるように、/posts/postのID
でDELETE、PATCHメソッドが利用できるようにしています。
import express from 'express';
import { addPost, deletePost, getPosts, updatePost } from '../../controllers/postController';
const router = express.Router();
// GET POST
router.route('/').get(getPosts).post(addPost);
// DELETE PATCH
router.route('/:postId').delete(deletePost).patch(updatePost);
export default router;
Controllerの実装
Routeで設定したパスに対してのGET、POST、DELETE、PATCHに対応するコントローラーを作成します。
なお、コントローラーには非同期通信を扱いやすくするミドルウェア「express-async-handler」を利用しています。
import express from 'express';
import asyncHandler from 'express-async-handler';
import mongoose from 'mongoose';
import Post from '../models/postModel';
export type PostType = {
_id: string;
createdAt: number;
updatedAt?: number;
title: string;
body: string;
image: string;
like: number;
publish: boolean;
};
/**
* Fetch all posts
* GET /posts
*/
export const getPosts = asyncHandler(async (req: express.Request, res: express.Response) => {
await Post.find({}, (error, post) => {
if (error) res.send(error);
res.json(post);
});
});
/**
* Add new post
* POST /posts
*/
export const addPost = asyncHandler(async (req: express.Request, res: express.Response) => {
const newPost = new Post(req.body as PostType);
await newPost.save((error, post) => {
if (error) res.send(error);
res.json(post);
});
});
/**
* Delete post
* DELETE /posts/:postId
*/
export const deletePost = asyncHandler(async (req: express.Request, res: express.Response) => {
await Post.deleteOne(
{
_id: req.params.postId,
},
(error) => {
if (error) res.send(error);
res.json({ message: 'Post successfully deleted' });
},
);
});
/**
* Update post
* PATCH /posts/:postId
*/
export const updatePost = asyncHandler(async (req: express.Request, res: express.Response) => {
await Post.findOneAndUpdate(
{
_id: req.params.postId,
},
req.body,
{ new: true },
(error, post) => {
if (error) res.send(error);
res.json(post);
},
);
});
簡易的ですが、404ページの処理と、500エラー時のエラーメッセージ表示ページをmiddlewareとして用意します。
import express from 'express';
type Error = {
message?: string;
};
const notFound = (req: express.Request, res: express.Response, next: express.NextFunction) => {
const error = new Error(`Not Found -${req.originalUrl}`);
res.status(404);
next(error);
};
const errorHandler = (
err: Error,
req: express.Request,
res: express.Response,
next: express.NextFunction,
) => {
const statusCode = res.statusCode === 200 ? 500 : res.statusCode;
res.status(statusCode);
res.json({
message: err.message,
});
};
export { notFound, errorHandler };
index.ts
ファイルにそれぞれ読み込むよう記載を追記します。
import express from 'express';
import cors from 'cors';
import postRoutes from './routes/v1/posts';
// エラーハンドリング用のミドルウェアを作成して読み込んでいます
import { notFound, errorHandler } from './middleware/errorMiddleware';
import connectDB from './config/db';
// DBと接続
connectDB();
const app = express();
// CORS設定
app.use(cors());
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// posts用のルーティング設定 /posts以下で別途設定したルートを使うよう指定します
app.use('/posts', postRoutes);
// エラーハンドリング用の処理を下記に追加しています
app.use(notFound);
app.use(errorHandler);
const port = process.env.PORT || 3001;
app.listen(port);
console.log('Express WebAPI listening on port ' + port);
これで今回利用する簡易APIのソースは完成です。
動作確認
サーバーを起動して早速APIを利用してみましょう。
$ yarn dev
動作の確認にはPostmanを使います。
GET
Postmanを起動して、メソッドにはGET
、URLにhttp://localhost:3001/posts
と記入して、Sendボタンを押します。
下部のBody欄に登録した記事の一覧が出力されれば取得成功です。
POST
次に投稿の登録を試してみます。メソッドにはPOST
、URLにhttp://localhost:3001/posts
と記入。
Body
にデータを記載して投げるので、Body
を選択、raw
、JSON
形式を選んでBodyに登録したいデータを記載します。
今回Modelではtitle
、body
、image
が記載必須で、その他の項目は初期値が割り当てられる、または自動的に値が付与される部分になるので、たとえば下記のようにデータを作成します。
{
"title": "投稿テスト",
"body": "本文のテスト",
"image": "image.png"
}
入力が完了したらSend
を押してデータを送ります。
下部のエリアに投稿したデータが返ってきたら成功です。
PATCH
次に投稿の更新を行います。メソッドにはPATCH
、URLにhttp://localhost:3001/posts/postの_id
と記入します。
今回投稿のIDはさきほど登録したデータの_id
を利用します。
試しに更新内容として下記のようにtitle
を入力してSend
ボタンを押します。
{
"title": "投稿テスト更新"
}
無事にtitle
が更新されました。
DELETE
最後に記事の削除を行います。メソッドにはDELETE
、URLにhttp://localhost:3001/posts/postの_id
と記入します。
投稿のIDはさきほど更新したデータの_id
を利用します。
削除時には送るデータはないのでそのままSend
を押します。
削除成功のメッセージが返ってきました。
さいごに
以上で簡易的ではありますが、CRUD操作のできるAPIが完成しました。実際の運用に利用するにはセキュリティ的な施策や、認証など機能を追加していく必要はありますが、このまま拡張していく形で用意することができると思います。
なお、完成版?のソースはこちらになります。
Discussion