初サイト制作メモ2: DB, APIを模した簡易実装をしてみる

※備忘録用。誤った記載があるかもしれません。コメントで教えていただければ嬉しいです

# DBに当たるものを直書きで用意してみる
typeは適当に定義し、それの配列としてramenListを作成
```src/db/ramen.ts
export type Ramen = {
  id: string;
  name: string;
  image?: string;
  custom?: {
    volume: string;
    toppings: string;
  };
  description?: string;
};

const ramenList: Ramen[] = [
  {
    id: "1",
    name: "Shoyu Ramen",
    image: "/images/shoyu-ramen.jpg",
    description:
      "Shoyu ramen is a Japanese noodle soup that is flavored with soy sauce. It is one of the most popular and classic ramen in Japan.",
  },
  {
    id: "2",
    name: "Miso Ramen",
    image: "/images/miso-ramen.jpg",
    description:
      "Miso ramen is a Japanese noodle soup that is flavored with miso. It is one of the most popular and classic ramen in Japan.",
  },
  {
    id: "3",
    name: "Tonkotsu Ramen",
    image: "/images/tonkotsu-ramen.jpg",
    description:
      "Tonkotsu ramen is a Japanese noodle soup that is flavored with pork bone broth. It is one of the most popular and classic ramen in Japan.",
  },
];

```

# APIを作ってみる
DBから値を取得したり値を追加したりする関数を、先ほどのファイルに追記
```src/db/ramen.ts
export function getRamenList(): Ramen[] {
  return ramenList;
}

export function getRamenById(id: string): Ramen | undefined {
  return ramenList.find((ramen) => ramen.id === id);
}

export function addRamen(ramen: Ramen) {
  ramenList.push(ramen);
}

export function deleteRamen(id: string) {
  const index = ramenList.findIndex((ramen) => ramen.id === id);
  if (index !== -1) {
    ramenList.splice(index, 1);
  }
}

export function updateRamen(ramen: Ramen) {
  const index = ramenList.findIndex((r) => r.id === ramen.id);
  if (index !== -1) {
    ramenList[index] = ramen;
  }
}
```

先ほどの関数を、各メソッドで呼び出すよう実装。

index.tsにはデータ全てを取得したり削除したりする処理を記述。
id指定で操作したい内容は、後の[id].tsに記載。
```src/pages/api/ramen/index.ts
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
//api配下のファイルは1つ1つがAPIエンドポイントとして機能する
import type { NextApiRequest, NextApiResponse } from "next";
import type { Ramen } from "@/db/ramen";
import { addRamen, getRamenList } from "@/db/ramen";

//習慣としてhandlerという名前にしている
export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<Ramen[]>
) {
  if (req.method === "GET") {
    res.status(200).json(getRamenList());
  } else if (req.method === "POST") {
    const ramen = req.body as Ramen;
    addRamen(ramen);
    res.status(201).json([ramen]);
  } else {
    //一括削除の時などの処理をここに書く
    res.status(404).end();
  }
}

```
[id].tsでは、id指定でデータ取得・削除などできるようにする

[id].tsのファイル名にすることで、動的パラメータを持つAPIエンドポイントを作成できる。
/api/ramen/の後ろに入る値が、req.queryで取得される。
```src/pages/api/ramen/[id].ts
import type { NextApiRequest, NextApiResponse } from "next";
import type { Ramen } from "@/db/ramen";
import { getRamenById, deleteRamen, updateRamen } from "@/db/ramen";

export default function handler(
  req: NextApiRequest,
  res: NextApiResponse<Ramen | undefined>
) {
  const { id } = req.query;
  if (!id || Array.isArray(id) || id.length === 0) {
    res.status(400).end();
    return;
  }
  if (req.method === "GET") {
    const ramen = getRamenById(id);
    if (ramen) {
      res.status(200).json(ramen);
    } else {
      res.status(404).end();
    }
  } else if (req.method === "PUT") {
    const ramen = req.body as Ramen;
    if (ramen.id !== id) {
      res.status(400).end();
      return;
    }
    updateRamen(ramen);
    res.status(200).json(ramen);
  } else if (req.method === "DELETE") {
    deleteRamen(id);
    res.status(204).end();
  } else {
    res.status(404).end();
  }
}

```

※importで@を使うためには、以下のようにエイリアスを設定しておく。
```tsconfig.json
{
  "compilerOptions": {
    "target": "es5",
    "lib": ["dom", "dom.iterable", "esnext"],
    "allowJs": true,
    "skipLibCheck": true,
    "strict": true,
    "noEmit": true,
    "esModuleInterop": true,
    "module": "esnext",
    "moduleResolution": "bundler",
    "resolveJsonModule": true,
    "isolatedModules": true,
    "jsx": "preserve",
    "incremental": true,
    "paths": {
      "@/*": ["./src/*"]
    }
  },
  "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
  "exclude": ["node_modules"]
}

```

# APIを叩いてみる
GETは、単にブラウザのURLに入れればOK
`GET api/ramen`
http://localhost:8888/api/ramen
※3000番は他で使っているため、`package.json`に`"dev": "next dev -p 8888"`と設定してある
※http://localhost:8888/api/ramen で開いたときにhandler内のGETメソッドが実行されるのは、Next.jsのAPIルート機能によるもの。exportされている関数を実行する。Next.jsでは、pages/apiディレクトリ内に配置されたファイルは、サーバー側で実行されるAPIエンドポイントとして扱われる

![スクリーンショット 2024-02-21 22.11.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366676/7fe6923c-8f5b-8b39-76ff-ae1fca345454.png)

他のメソッドは、[Postman](https://www.postman.com/)で試す。
![スクリーンショット 2024-02-21 22.16.59.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366676/300800cf-7863-0f9e-2449-5bb3b55afdc1.png)
![スクリーンショット 2024-02-21 22.17.13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2366676/0bd52466-267d-2610-d4c8-baf0c9ba01ec.png)