Express で Routing を実装

# はじめに
この記事では、ブログアプリのCRUD機能の実装を通して、Node.jsのWebアプリケーションフレームワークである [_**Express**_](https://expressjs.com/) の Routing について学んでいきます。

# 開発環境
開発環境は以下の通りです。
- Windows11
- VSCode
- Talend API Tester
- TypeScript 5.0.4
- Node.js 18.14.0
- Express 4.18.2

# 開発環境の構築
プロジェクトフォルダの作成からローカルサーバーの起動まで行います。

プロジェクトフォルダの作成し、```package.json``` を作成します。
```
mkdir blog-api
cd blog-api
yarn init -y
```
Express をインストールします。
```
yarn add express
```
TypeScritpを使うために必要なライブラリをインストールします。
```
yarn add -D typescript @types/node ts-node @types/express
```
```tsconfig.json``` を作成します。
```
yarn tsc --init
```
コード変更時に自動でサーバーを再起動してくれる [_**nodemon**_](https://nodemon.io/) をインストールします。
```
yarn add -D nodemon
```
```package.json``` にローカルサーバー起動スクリプトを追加します。
```package.json
"scripts": {
    "start": "nodemon index.ts"
  },
```

```index.ts``` を作成し、ログを出力するコードを書きます。

```index.ts
import express from "express";

const app: Application = express();
const port = 3001;

app.use(express.json());

app.listen(port, () => {
  console.log(`listening on port ${port}`);
});
``` 
まずはじめに [_**express()**_](https://expressjs.com/en/4x/api.html#express) メソッドで Express のインスタンスを作成します。インスタンスを作成することで、Express の各メソッドが利用できるようになります。

次に利用するポートを指定します。今回は ```3001``` を利用します。

```app.use(express.json());``` で、リクエストボディの JSON をパースできるようにしておきます。[_**express.json**_](https://expressjs.com/en/4x/api.html#express.json) が JSON をパースできるようにするミドルウェアです。express.json を [_**app.use**_](https://expressjs.com/en/4x/api.html#app.use) の引数に渡して、プログラム上にマウントします。

最後に [_**app.listen()**_](https://expressjs.com/en/4x/api.html#app.listen) メソッドで利用中のポートをコンソールログに出力するようにします。

それでは、ローカルサーバーを起動して動作確認をします。
```
yarn start
```
ログが出力されたことを確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/02784e10-9b61-db4a-6ccb-21cacb6b7ead.png)

# CRUD 機能の実装
ここから CRUD の実装に入っていきます。
各機能とも以下の形式で実装します。

[```app.METHOD(PATH, HANDLER)```](https://expressjs.com/en/4x/api.html#app.METHOD)

:::note
METHODを Express で利用する際は、小文字（ex. get、post）で記載します。
:::

# Read
まずは Read（記事一覧取得）機能を実装します。

```index.ts
import express, { Application, Request, Response } from "express";

const app: Application = express();
const port = 3001;

app.use(express.json());

interface Blog {
  id: string;
  title: string;
  body: string;
}

const sampleBlogs: Blog[] = [
  {
    id: "sample1",
    title: "Sample Blog Title 1",
    body: "This is a sample blog body 1.",
  },
  {
    id: "sample2",
    title: "Sample Blog Title 2",
    body: "This is a sample blog body 2.",
  },
];

let blogs: Blog[] = sampleBlogs;

// Get all blogs
app.get("/blogs", (req: Request, res: Response) => {
  res.json(blogs);
});
```
[_**app.get()**_](https://expressjs.com/en/4x/api.html#app.get.method) メソッドのハンドラーで、[_**res.json()**_](https://expressjs.com/en/4x/api.html#res.json) メソッドを使い、JSON形式のレスポンスを返します。

[_**Talend API Tester**_](https://chrome.google.com/webstore/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm?hl=ja) を利用して動作確認をします。
一覧を取得できることを確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/9a94c117-a795-1fc5-8969-ac700fb0fbc4.png)

# Create
次は Create（新規記事作成）機能を実装します。

```index.ts
// Create a new blog
app.post("/blogs", (req: Request, res: Response) => {
  const newBlog: Blog = {
    id: Date.now().toString(),
    title: req.body.title,
    body: req.body.body,
  };

  blogs.push(newBlog);

  res.status(201).json(newBlog);
});
```

[_**app.post()**_](https://expressjs.com/en/4x/api.html#app.post.method) メソッドのハンドラーで現在時刻とリクエストボディから受け取った記事のタイトル、本文から新しい記事を作成します。そして、新しい記事を記事一覧の配列に追加した後、レスポンスを返します。また、[_**res.status()**_](https://expressjs.com/en/4x/api.html#res.status) で、HTTP のステータスコードを返すことができます。

動作確認をして、新規作成できることを確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/c2aa08c4-58d8-5b8e-e5ce-e9506792c43e.png)

# Update
次は Update（記事更新）機能を実装します。

```index.ts
// Update a blog
app.put("/blogs/:id", (req: Request, res: Response) => {
  if (!blogs.find((blog) => blog.id === req.params.id)) {
    return res.status(404).json({ error: "Blog Not  Found" });
  }

  const updatedBlog = {
    id: req.params.id,
    title: req.body.title,
    body: req.body.body,
  };

  res.status(200).json(updatedBlog);
});
```

[_**app.put()**_](https://expressjs.com/en/4x/api.html#app.put.method) メソッドのハンドラーで、パラメーターのIDが現在のブログ記事一覧に含まれているか確認します。含まれていない場合、404を返します。含まれている場合、更新された記事を生成し、レスポンスを返します。

動作確認をして、更新できることを確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/b08b6f90-52a5-c35c-d917-711c21f647b3.png)

# Delete
最後に Delete（記事削除）機能を実装します。

```index.ts
// Delete a blog
app.delete("/blogs/:id", (req: Request, res: Response) => {
  if (!blogs.find((blog) => blog.id === req.params.id)) {
    return res.status(404).json({ error: "Blog Not  Found" });
  }

  const remainedBlogs = blogs.filter((blog) => blog.id !== req.params.id);

  res.json(remainedBlogs);
});
```
[_**app.delete()**_](https://expressjs.com/en/4x/api.html#app.delete.method) メソッドのハンドラーでパラメーターのIDのブログ記事を除いた記事一覧のレスポンスを返します。

動作確認をして、削除できることを確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/318188/334edf5e-917e-ae1f-20c7-2e7770d65b41.png)

# 参考
- [Express](https://expressjs.com/)
- [nodemon](https://nodemon.io/)
- [Talend API Tester](https://chrome.google.com/webstore/detail/talend-api-tester-free-ed/aejoelaoggembcahagimdiliamlcdmfm?hl=ja)