TS, JS: fetchをミスなく快適に書く方法（fetcha!）

# 通常のfetchの例

コメントで問題点を記載
```typescript
fetch("https://example.com/api", {
  method: "POS", // 1. エディタ上でエラーにならない
  headers: {
    "Content-Typ": "application/jso", // 2. エディタ上でエラーにならない
    Authorization: `Bearer ${"foo"}`,
  },
  credentials: "include",
  mode: "cors",
  body: JSON.stringify({ name: "bar" }), // 3. JSON.stringify が必要
})
  .then((resp) => {
    if (resp.ok) return resp.json();

    // 4. then なのにエラー処理が入る
    switch (resp.status) {
      case 500:
        location.href = "/500.html";
        return;
      case 404:
        location.href = "/404.html";
        return;
      case 401:
        location.href = "/login.html";
        return;
      default:
        alert(resp.statusText);
    }
  })
  .then(({ id }: { id: number }) => console.log(id))
  .catch((e) => alert(e.message));
```

# 解決

コード補完が効くので、ミスなく快適に
```typescript
fetcha("https://example.com/api")
  .contentType("application/json")
  .header("Authrization", `Bearer ${"foo"}`)
  .credentials("include")
  .mode("cors")
  .post({ name: "bar" })
  .then((resp) => resp.toJson<{ id: number }>())
  .then(({id}) => console.log(id))
  .catch((e: FetchaError) => {
    switch (e.status) {
      case 500:
        location.href = "/500.html";
        return;
      case 404:
        location.href = "/404.html";
        return;
      case 401:
        location.href = "/login.html";
        return;
      default:
        alert(e.message);
    }
  });
```

# 利用方法

```sh
npm i @co-labo-hub/fetcha
```

```typescript
import { fetcha, FetchaError } from '@co-labo-hub/fetcha';
```


# 他

# bodyの渡し方

- Content-Type が application/json で始まっている場合、
かつ body が objectの場合、JSON.stringify が実行されます

```typescript
fetcha('https://example.com/api')
    .contentType('application/json')
    .post({name: 'foo'})
```

- body が Blob、FormData、URLSearchParams だった場合、Content-Type がクリアされます
参考：https://zenn.dev/kariya_mitsuru/articles/25c9aeb27059e7
```typescript
const form = new URLSearchParams();
form.set("name", "foo");
fetcha('https://example.com/api')
    .contentType('application/json')  // 一致しないため無視される
    .post(form)
```

# ヘッダの指定方法
Content-Type は contentType メソッドで設定できますが、引数は空文字列か application/json の2択です。
参考：https://m0rter.anyfrog.net/2019/56/
空文字列の場合はヘッダから Content-Type を削除します。
他の Content-Type を設定する場合は header メソッドが使えます。
```typescript
fetcha('https://example.com/api')
    .header('Content-Type', 'text/plain')
```

## メソッドの指定方法

fetch実行は下記メソッドが利用できます。
{method: string} でのタイプミスが避けられます。
参考：https://www.ohitori.fun/entry/how-to-fix-a-bug-in-the-fetch_api-patch-request

```typescript
.get()
.head()
.delete()
.post(body?)
.patch(body?)
.put(body?)

//または
.fetch(method: "GET" | "POST" | "PATCH" | "PUT" | "HEAD" | "DELETE" = "GET")
```

## JSON返値の型指定
```typescript
// 標準のresp.json()の場合
const data = await fetcha('https://example.com/api')
    .get()
    .then(resp => resp.json() as Promise<{id: number}>)

// resp.toJson<T>()の場合
const data = await fetcha('https://example.com/api')
    .get()
    .then(resp => resp.toJson<{id: number}>());
```

## URLの指定方法

柔軟に対応可能

```typescript
//1
fetcha('https://example.com/api')
//2
fetcha().url('https://example.com/api')
//3
fetcha(new URL('/api', 'https://example.com/'))
//4
fetcha('/api')
    .origin('https://example.com/')
//5
fetcha()
    .origin('https://example.com/')
    .url('/api')
```

## エラー処理
エラーオブジェクトにレスポンスの各種プロパティが同名で含まれており
HTTPステータスコードを見て処理の振り分けなどが可能

```typescript
.post()
.then(/* ... */)
.catch((e: FetchaError) => {
    switch (e.status) {
      case 500:
        location.href = "/500.html";
        return;
      case 404:
        location.href = "/404.html";
        return;
      case 401:
        location.href = "/login.html";
        return;
      default:
        alert(e.statusText);
    }
})
```

## 詳細

https://github.com/co-labo-hub/fetcha

サンプル
https://github.com/co-labo-hub/fetcha/blob/main/src/example.ts

# 最後に

fetchは実行前の組み立てが面倒ですが
エディタ上でメソッドの候補が表示され
選択していくことでメソッドチェーンが組み上がるのは気持ちいいです。

また、fetchは実行後のエラー処理が汚くなりがちですが、
catch内で必要な情報が揃った状態で処理が書けるので
クリーンなコードになるように思います。