Nuxt3のServer Directoryで利用できるリクエスト一覧

# はじめに

Nuxt3のServer APIで利用可能なリクエスト一覧です。

本記事で扱うデータの取得取得方法は下記の4つ。

- クエリパラメータ
- リエクエストボディ
- パスパラメータ
- マルチパートフォーム

サーバーAPIの公式ドキュメント

https://nuxt.com/docs/guide/directory-structure/server#api-routes

## クエリパラメータ

クエリパラメータはドキュメントに記載がある為、そちらに従って実装する。

- クライアント側

```vue
<script setup lang="ts">
const res = await useFetch("/api/hoge?param1=abcd", {
    method: "GET",
    headers: { "Content-Type": "application/json" },
});
</script>
```

- サーバ側

サーバ側もドキュメントに従い`getQuery`メソッドを利用して取得する。nuxt3の自動インポート機能により`import`文は不要。

例として以下のフォルダ構成で`index.get.ts`を作成する。

```
server/ 
  | - api/ 
       |- hoge/
           | - index.get.ts
```

```typescript:index.get.ts
export default defineEventHandler(async (event) => {
    const query = getQuery(event)
    console.log(query.param1); // 「abcd」と出力される
});
```

サーバ側では`query.param1=abcd`として値を取得できる。

## リクエストボディ

リクエストボディから値を取得する方法もドキュメントに記載があるため、その通りに実装する。

- クライアント側

リクエストボディの値は`useFetch`の`body`プロパティに設定する。

```vue
<script setup lang="ts">
const res = await useFetch("/api/hoge", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ comment: "test" }),
});
</script>
```

- サーバ側

サーバ側でリクエストボディの値を取得する際は`readBody`を利用する。

例として以下のフォルダ構成で`index.post.ts`を作成する。

```
server/ 
  | - api/ 
       |- hoge/
           | - index.post.ts
```


```typescript:index.post.ts
export default defineEventHandler(async (event) => {
    const body = await readBody(event); // readBodyメソッドを利用
    console.log(body) // 「{ comment: "test" }」と出力される
});
```

サーバ側では`body={ comment: "hoge" }`として値を取得できる。


## パスパラメータ

サーバAPI側のパスパラメータの取得処理は、フロントのルーティングと似たルールで実装することで実現可能。

- クライアント側

```vue
<script setup lang="ts">
const res = await useFetch("/api/hoge/abcd", {
    method: "GET",
    headers: { "Content-Type": "application/json" },
});
</script>
```

- サーバ側

パスパラメータとして値を受け取る為に、フロント側と同様に`server/api`配下に、`[id].${メソッド名}.ts`の命名規則でファイルを作成する。
これにより、サーバ側では`event.context.params?.id`と指定することで、`[id]`に指定した値にアクセスできる。

例として、下記の様なフォルダ構成で`[id].get.ts`を作成し処理を実装する。

```
server/ 
  | - api/ 
       |- hoge/
           | - [id].get.ts
```

```typescript:[id].get.ts
export default defineEventHandler(async (event) => {
    const id: string | undefined = event.context.params?.id;
    console.log(id); // 「abcd」と出力される
});
```

実行することでクライアント側の`/api/hoge/abcd`の`abcd`の部分が、サーバ側で`event.context.params.id`に格納される。


:::note
`[id]`の部分は他の文字列でも問題ない。例えば`testId`の変数名で値を取得したい場合は、`[id].get.ts`ではなく`[testId].get.ts`の名称でファイルを作成する。

その場合、APIの実装の方も`testId`に変更する必要がある。

```typescript:[testId].get.ts
export default defineEventHandler(async (event) => {
    // const id = event.context.params?.id;
    const testId: string | undefined = event.context.params?.testId;
    console.log(testId); 
});
```
:::


## マルチパートフォーム

画像や動画の様な、メディアファイルのアップロードを実装する際に利用する。

マルチパートフォームに関してはNuxt3のドキュメントの方には記載が見当たらず(2023/07/21現在)、Nuxt3のサーバ側のライブラリであるh3のドキュメントを眺めている際に発見した。

https://www.jsdocs.io/package/h3

- クライアント側

メディアデータを送信するクライアント側は、`body`の引数に`FormData()`を渡す。 `formData`に値を追加する際のキーは、サーバーAPIで取得するときに検索するキーとなる為、サーバーAPIの定義と同じにする必要がある。

以下の例は、メディアファイルとjson形式の２つのデータを、マルチパートフォームを利用してサーバに送信する例である。

```vue
<script setup lang="ts">
const formData = new FormData();

formData.append("body", JSON.stringify({comment: "test" }));
formData.append("file", ${fileDataURL}); // 送信するメディアファイルのdataURLを設定する 

const res = await useFetch(
  `/api/hoge`,
  {
    method: "POST",
    body: formData,
  }
)
</script>
```

- サーバ側

サーバ側でマルチパートフォームでリクエストされた値を取得する際に、`readMultipartFormData`を利用する。
Nuxt3の自動インポート機能の対象な為`import`文は不要。

リクエストボディの際と同様に、下記の様なフォルダ構成で`index.post.ts`を作成する。

```
server/ 
  | - api/ 
       |- hoge/
           | - index.post.ts
```

```typescript
export default defineEventHandler(async (event) => {
  const data = await readMultipartFormData(event!);

  // リクエストボディとファイルを取得
  let body: any | undefined = undefined;
  let file: Buffer | undefined = undefined;

  for (const d of data) {
    if (d.name === "body") {
      // クライアント側でformDataに「body」で登録した値
      body = {
        ...JSON.parse(d.data.toString()),
      };
    } else if (d.name === "file") {
      // クライアント側でformDataに「file」で登録した値
      file = d.data;
    }
  }
});
```

`file`にアップロードしたファイルが`Buffer`形式で格納され、`body`には`{comment: "test" }`が格納される。