プログラミング

[開発記録]TypeScript+Express+Postgresqlで、簡易的なAPIサーバを構築した話⑤ -CRUD処理を一通り実装した話-

「TypeScript Express PostgreSQL」5本目です。
前回、GETリクエストのみ実装したので、CRUD処理すべて実行するために、POST/PATCH/DELETEリクエストを実装しました。

CRUD処理

DBの用語です。データ操作のうち、基本的な下記の処理をまとめた総称です。

  • Create…データ作成
  • Read…データ読み取り
  • Update…データ更新
  • Delete…データ削除

REST APIのメソッドとは、下記のように紐づけらることが多いです。

CreatePOST or PUT
ReadGET
UpdatePOST or PATCH
DeleteDELETE

Create / Updateをどのメソッドに紐づけるかは割とお好みです。場合により、CRUDすべてをPOSTメソッドで定義しているシステムもあったように思います。

今回作るシステムでは、Create->POST、Update->PATChとして作成しています。

実装

作成していきます。READの処理はすでに実装しているので、これに合わせてRoutingとControllerを実装していきます。

Routingリファクタリング

Routeを追加し、ControllerからAPI処理をインポートして紐づけます。
DELETE、PATCH処理はユーザIDをパスで指定する形で設定します。パスのIDを変えれば自由にDBの情報が閲覧できるため、商用システムで利用するには少し脆弱ですね。でも今回はサンプルで作るだけなので、このままいきます。

application/routes/index.ts

import { Router } from "express";

// domain/controllers/userで実装したAPI処理をインポート
import { getUsers, postUser, deleteUser, patchUser } from "../../domain/controllers/user";

// Router設定
const router = Router();

// API処理を登録
router.get("/users", getUsers); // get user
router.post("/users", postUser); // register user
router.delete("/users/:id", deleteUser); // delete user
router.patch("/users/:id", patchUser); // update user

export default router;

Controllerリファクタリング

各メソッドの処理を定義します。
GETの処理をコピペして、TypeORMの機能を利用して実装していきます。

domain/controllers/user.ts

import { RequestHandler } from "express";
import {
  createConnection,
  CustomRepositoryCannotInheritRepositoryError,
  getRepository,
  Repository,
} from "typeorm";

import dbConfig from "../../infrastructure/db-config";
import User from "../entities/user";

// ユーザ登録 POST /users
export const postUser: RequestHandler = (req, res, next) => {
  console.log("API[postUser] START");

  const userName: string = req.body.name;
  const userAge: number = req.body.age;

  // PostgreSQL に接続を行い。顧客情報を取得する。
  // Parameterチェック処理が不足
  createConnection(dbConfig)
    .then(async (connection) => {
      console.log("PostgreSQL Connected");

      try {
        const userRepository: Repository<User> = getRepository(User);

        const query = userRepository.create({
          name: userName,
          age: userAge,
        });

        const query_result = await userRepository.save(query);

        res.status(200).json({ response: query_result });
        console.log("Register : ", query_result.toString());
      } catch (error) {
        console.error("Failed : ", error);
        res.status(404).json({ message: "データ登録に失敗しました。" });
      } finally {
        await connection.close();
        console.log("Connection Closed");
      }
    })
    .catch((error) => {
      console.error("PostgreSQL Connection Failed", error);
      res.status(500).json({ message: "DBの接続に失敗しました。" });
    });

    console.log("API[postUser] END");
};

// ユーザ一覧取得 GET /users
export const getUsers: RequestHandler = (req, res, next) => {
  console.log("API[getUsers] START");
  // PostgreSQL に接続を行い。顧客情報を取得する。
  createConnection(dbConfig)
    .then(async (connection) => {
      console.log("PostgreSQL Connected");

      try {
        const userRepository: Repository<User> = getRepository(User);

        const allCustomers = await userRepository.find();
        res.status(200).json({ response: allCustomers });
        console.log("Select : ", allCustomers);
      } catch (error) {
        console.error("Failed : ", error);
        res.status(404).json({ message: "データの取得に失敗しました。" });
      } finally {
        await connection.close();
        console.log("Connection Closed");
      }
    })
    .catch((error) => {
      console.error("PostgreSQL Connection Failed", error);
      res.status(500).json({ message: "DBの接続に失敗しました。" });
    });

    console.log("API[getUsers] END");
};

// ユーザ削除 DELETE /users/:id
export const deleteUser: RequestHandler<{ id: string }> = (req, res, next) => {
  console.log("API[deleteUsers] START");
  // PostgreSQL に接続を行い。顧客情報を取得する。
  const userId = req.params.id;

  createConnection(dbConfig)
    .then(async (connection) => {
      console.log("PostgreSQL Connected");

      try {
        const userRepository: Repository<User> = getRepository(User);
        const query_result = await userRepository.delete(userId);

        res.status(200).json({ response: query_result });
        console.log("Select : ", query_result);
      } catch (error) {
        console.error("Failed : ", error);
        res.status(404).json({ message: "データの取得に失敗しました。" });
      } finally {
        await connection.close();
        console.log("Connection Closed");
      }
    })
    .catch((error) => {
      console.error("PostgreSQL Connection Failed", error);
      res.status(500).json({ message: "DBの接続に失敗しました。" });
    });

    console.log("API[deleteUsers] END");
};

// ユーザ更新 PATCH /users/:id
export const patchUser: RequestHandler = (req, res, next) => {
  console.log("API[patchUser] START");

  const userId = req.params.id;
  const userName: string = req.body.name;
  const userAge: number = req.body.age;

  // PostgreSQL に接続を行い。顧客情報を取得する。
  // Parameterチェック処理が不足
  createConnection(dbConfig)
    .then(async (connection) => {
      console.log("PostgreSQL Connected");

      try {
        const userRepository: Repository<User> = getRepository(User);

        const query_result = await userRepository.update(userId, {name: userName, age: userAge});

        res.status(200).json({ response: query_result });
        console.log("Register : ", query_result.toString());
      } catch (error) {
        console.error("Failed : ", error);
        res.status(404).json({ message: "データ登録に失敗しました。" });
      } finally {
        await connection.close();
        console.log("Connection Closed");
      }
    })
    .catch((error) => {
      console.error("PostgreSQL Connection Failed", error);
      res.status(500).json({ message: "DBの接続に失敗しました。" });
    });

    console.log("API[patchUser] END");
};

まとめ

ユーザデータのCRUD処理が実装できました。脆弱な部分はあるものの、シンプルに実装できたように思います。この次はロギングやエラー処理、DB接続周りをリファクタリングしつつ、オブジェクト指向の処理に続々変えていきます。

以上です。