Sequelize TypeScript と Express.js を使用してタイプ セーフな API を構築する


概要



Sequelize は Node ユニバースで最も人気のある ORM だと思います.ただし、JavaScript コミュニティでは広く使用されていますが、TypeScript コミュニティではあまり使用されていません.現在では、より優れたサポートと開発経験を持ついくつかの代替手段があるためです.

しかし、私の意見では、sequelize-typescript 依存関係の存在によってすべてが変わります.エンティティの構造化方法とデータベースへの接続方法がより直感的になります.スタックを変更せずに、はるかに便利な方法で JavaScript から TypeScript に移行できるため、このエクスペリエンスは理想的です.

今日の例



今日の例では、TypeScript を使用して Node プロジェクトをセットアップします.次に、Express.js フレームワークを使用して API を作成し、アプリケーションの CRUD を作成します.

プロジェクトのセットアップ



最初のステップとして、プロジェクト ディレクトリを作成し、そこに移動します.

mkdir ts-sequelize
cd ts-sequelize


次に、TypeScript プロジェクトを初期化し、必要な依存関係を追加します.

npm init -y
npm install typescript ts-node-dev @types/node --save-dev


次に、tsconfig.json ファイルを作成し、次の構成を追加します.

{
  "compilerOptions": {
    "sourceMap": true,
    "outDir": "dist",
    "strict": true,
    "lib": [
      "esnext"
    ],
    "esModuleInterop": true,
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "target": "esnext",
    "moduleResolution": "Node",
  }
}


次のスクリプトを package.json ファイルに追加しましょう.

{
  // ...
  "type": "module",
  "scripts": {
    "start": "ts-node-dev main.ts"
  },
  // ...
}


次に、Express と Sequelize の依存関係 (およびそれらの開発の依存関係) のインストールに進みます.

npm install express mariadb reflect-metadata sequelize sequelize-typescript --save
npm install @types/express @types/validator --save-dev


コーディングしよう



それでは、簡単な API を作成しましょう.

// @/main.ts
import "reflect-metadata";
import express, { Request, Response } from "express";

const app = express();

app.use(express.json());

app.get("/", (req: Request, res: Response): Response => {
  return res.json({ message: "Sequelize Example 🤟" });
});

const start = async (): Promise<void> => {
  try {
    app.listen(3000, () => {
      console.log("Server started on port 3000");
    });
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

void start();


ポート 3000 で API を初期化するには、次のコマンドを実行します.

npm start


ここで、今日の例からエンティティを作成することから始めましょう.最近行っているように、4 本足の友人の名前、人種、年齢、そして彼が良い人だったかどうかなどのフィールドを持つ Dog というモデルを作成しましょう.男の子かどうか.テンプレートは次のようになります.

// @/models.ts
import { Table, Model, Column, DataType } from "sequelize-typescript";

@Table({
  timestamps: false,
  tableName: "dogs",
})
export class Dog extends Model {
  @Column({
    type: DataType.STRING,
    allowNull: false,
  })
  name!: string;

  @Column({
    type: DataType.STRING,
    allowNull: false,
  })
  breed!: string;

  @Column({
    type: DataType.BOOLEAN,
    allowNull: true,
    defaultValue: true,
  })
  isGoodBoy!: boolean;
}


定義したモデルを使用して、データベース接続の構成に取り掛かることができます.

// @/connection.ts
import { Sequelize } from "sequelize-typescript";

import { Dog } from "./models";

const connection = new Sequelize({
  dialect: "mariadb",
  host: "localhost",
  username: "root",
  password: "root",
  database: "sequelize",
  logging: false,
  models: [Dog],
});

export default connection;


テンプレートを定義し、接続を構成したら、ノード インスタンスの起動時に接続を初期化するために main.ts に移動するだけで十分です.

// @/main.ts
import "reflect-metadata";
import express, { Request, Response } from "express";

import connection from "./database";

const app = express();

// ...

const start = async (): Promise<void> => {
  try {
    await connection.sync();
    app.listen(3000, () => {
      console.log("Server started on port 3000");
    });
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

void start();


すべての準備が整ったので、アプリケーションの CRUD の作業を開始できます.まず、データベースにあるすべての犬を取得するルートを作成しましょう.

app.get("/dogs", async (req: Request, res: Response): Promise<Response> => {
  const allDogs: Dog[] = await Dog.findAll();
  return res.status(200).json(allDogs);
});


次に、リクエスト パラメータで送信された ID から犬を探しましょう.

app.get("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
  const { id } = req.params;
  const dog: Dog | null = await Dog.findByPk(id);
  return res.status(200).json(dog);
});


次に、データベース テーブルに新しいレコードを挿入する必要があります.このために、リクエストボディからデータを送信します.

app.post("/dogs", async (req: Request, res: Response): Promise<Response> => {
  const dog: Dog = await Dog.create({ ...req.body });
  return res.status(201).json(dog);
});


次に、レコードを更新する必要があります.このために、id を介して更新を実行し、リクエスト本文で送信されるそれぞれのプロパティのフィールドを更新します.

app.put("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
  const { id } = req.params;
  await Dog.update({ ...req.body }, { where: { id } });
  const updatedDog: Dog | null = await Dog.findByPk(id);
  return res.status(200).json(updatedDog);
});


すべてのレコードを取得し、新しいレコードを作成して、特定のレコードを更新できるようになりました.データベース テーブルから特定のレコードを削除する必要があります. API の他のルートと同様に、id を介して実行しましょう.

app.delete("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
    const { id } = req.params;
    const deletedDog: Dog | null = await Dog.findByPk(id);
    await Dog.destroy({ where: { id } });
    return res.status(200).json(deletedDog);
  }
);


データベースからレコードを更新および削除するためのエンドポイントで気づいたかもしれませんが、更新または削除された要素のデータは、実行されたアクションに関する何らかのフィードバックを得るために、応答本文で返されます.
main.ts の最終的なコードは次のとおりです.

// @/main.ts
import "reflect-metadata";
import express, { Request, Response } from "express";

import connection from "./database";
import { Dog } from "./models";

const app = express();

app.use(express.json());

app.get("/dogs", async (req: Request, res: Response): Promise<Response> => {
  const allDogs: Dog[] = await Dog.findAll();
  return res.status(200).json(allDogs);
});

app.get("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
  const { id } = req.params;
  const dog: Dog | null = await Dog.findByPk(id);
  return res.status(200).json(dog);
});

app.post("/dogs", async (req: Request, res: Response): Promise<Response> => {
  const dog: Dog = await Dog.create({ ...req.body });
  return res.status(201).json(dog);
});

app.put("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
  const { id } = req.params;
  await Dog.update({ ...req.body }, { where: { id } });
  const updatedDog: Dog | null = await Dog.findByPk(id);
  return res.status(200).json(updatedDog);
});

app.delete("/dogs/:id", async (req: Request, res: Response): Promise<Response> => {
    const { id } = req.params;
    const deletedDog: Dog | null = await Dog.findByPk(id);
    await Dog.destroy({ where: { id } });
    return res.status(200).json(deletedDog);
  }
);

const start = async (): Promise<void> => {
  try {
    await connection.sync();
    app.listen(3000, () => {
      console.log("Server started on port 3000");
    });
  } catch (error) {
    console.error(error);
    process.exit(1);
  }
};

void start();


結論



いつものように、面白いと思っていただければ幸いです.この記事の誤りに気づいた場合は、コメントで指摘してください. 🧑🏻‍💻

良い一日を! 🧦