🙆‍♀️

ExpressとmongoDBを使ったREST APIをTypeScriptで組んでみる【3.CRUD操作編】

2021/06/21に公開

はじめに

前回作成したPostモデルを操作するためのコントローラーを作成します。
まず/postsにアクセスして操作ができるようにルーティングから設定します。

https://zenn.dev/himorishige/articles/2b2b7461939162
https://zenn.dev/himorishige/articles/5b645e7eb45f3e

Routeの設定

/posts/でget、POSTメソッドを利用できるように、/posts/postのIDでDELETE、PATCHメソッドが利用できるようにしています。

src/routes/v1/posts.ts
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」を利用しています。

https://www.npmjs.com/package/express-async-handler/v/1.1.4

src/controllers/postController.ts
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として用意します。

src/middleware/errorMiddleware
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ファイルにそれぞれ読み込むよう記載を追記します。

src/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を使います。

https://www.postman.com/

GET

Postmanを起動して、メソッドにはGET、URLにhttp://localhost:3001/postsと記入して、Sendボタンを押します。
下部のBody欄に登録した記事の一覧が出力されれば取得成功です。

POST

次に投稿の登録を試してみます。メソッドにはPOST、URLにhttp://localhost:3001/postsと記入。
Bodyにデータを記載して投げるので、Bodyを選択、rawJSON形式を選んでBodyに登録したいデータを記載します。

今回Modelではtitlebodyimageが記載必須で、その他の項目は初期値が割り当てられる、または自動的に値が付与される部分になるので、たとえば下記のようにデータを作成します。

{
  "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が完成しました。実際の運用に利用するにはセキュリティ的な施策や、認証など機能を追加していく必要はありますが、このまま拡張していく形で用意することができると思います。

なお、完成版?のソースはこちらになります。

https://github.com/himorishige/techBlog-backend

GitHubで編集を提案

Discussion