kintoneカスタマイズでtry/catchを使おう

## はじめに
皆さんこんにちは。サイボウズ株式会社テクニカルトレーナーのTeruです。
今回はkintoneカスタマイズにおけるtry/catchの使い方を考えていきましょう！

## try/catchの基本
そもそも、try/catchは例外が発生した場合に例外を処理するために使用します。
```JavaScript
try {
 実行する処理
} catch(e) {
 try文で例外が発生した場合に実行される処理
}
```

例外とは、kintoneカスタマイズにおいては多くの場合 REST API の実行に失敗した場合のことです。

### サンプル
例えば、`アプリ番号:１ レコード番号:１`のレコードを取得する場合は下記のようにtry/catchを使用します。

```JavaScript
try {
  // レコード取得処理
  await kintone.api('/k/v1/record', 'GET', { app: 1, id: 1 });
} catch (e) {
  // レコード取得に失敗した場合の処理
  console.error(e);
}
```

この取得処理でエラーが発生するのは、次のようなケースです。
- アプリが存在しない
- レコードが存在しない
- ネットワークエラー

仮にアプリが存在しなければ、`console.error(e)`はこのようなエラーを表示します。
`{code: 'GAIA_AP01', id: '8BwNVgSl5Z0XNrXaZbZI', message: '指定したアプリ（id: 1）が見つかりません。削除されている可能性があります。'}`

ちなみに`catch(e)`の`e`部分は自由に命名できますし、`catch`内で使用しなければ省略することもできます。
### 省略する場合のサンプル
```JavaScript
try {
  // レコード取得処理
  await kintone.api('/k/v1/record', 'GET', { app: 1, id: 1 });
} catch {
  // レコード取得に失敗した場合の処理
  console.error('取得に失敗しました');
}
```

## 複数の処理の扱い方

次に、複数の REST API を実行する場合のtry/catchについて扱っていきます。
例えば次のようなケースです。
- `アプリ番号:１ レコード番号:１`のレコードを取得する
- 取得したレコードの値を用いて、`アプリ番号:２`にレコードを登録する

### try/catchでひとまとめにするパターン
```JavaScript
try {
  // レコード取得処理
  const resp = await kintone.api('/k/v1/record', 'GET', { app: 1, id: 1 });
  // レコード登録処理
  const params = {
    app: 2,
    record: {
      氏名: {
        value: resp.record.氏名.value,
      },
    },
  };
  await kintone.api('/k/v1/record', 'POST', params);
} catch (e) {
  // 失敗した場合の処理
  console.error(e);
}
```

この場合は、取得処理・登録処理のどちらかにエラーが発生した瞬間に`catch`へ飛びます。
処理が正常に終了しなかったことをユーザーに対してアラートを表示したり、コンソールにエラーを表示することができます。

しかし、このコードではどちらでエラーが発生したのかは`catch`内で判別ができません。
「取得に失敗しました」や「登録に失敗しました」といったエラー文の使い分けができないことになります。
では、それぞれにtry/catchを設定すれば良いのでは？と考える人もいるかもしれません。

### try/catchを個別に設定するパターン（動作しません）
```JavaScript
try {
  // レコード取得処理
  const resp = await kintone.api('/k/v1/record', 'GET', { app: 1, id: 1 });
} catch (e) {
  // レコード取得に失敗した場合の処理
  console.error(e);
}

try {
  // レコード登録処理
  const params = {
    app: 2,
    record: {
      氏名: {
        value: resp.record.氏名.value,
      },
    },
  };
  await kintone.api('/k/v1/record', 'POST', params);
} catch (e) {
  // レコード登録に失敗した場合の処理
  console.error(e);
}
```
今回のコードは動きません。
`try`は`{}`でスコープ（＝変数がアクセスできる範囲）を作り出しています。
スコープ内で宣言された変数は外からアクセスすることができません。
よって、登録処理側で`resp`にアクセスすることができないわけですね。
まあ一応解決策として`resp`を外で`let`を使って先に宣言してしまう方法もあるのですが、エラー処理のためにエラーが発生する要因を作ることになりかねないので非推奨です。

ではどのようにすればいいでしょうか。

### await/catchを使うパターン
```JavaScript
// レコード取得処理
const resp = await kintone.api('/k/v1/record', 'GET', { app: 1, id: 1 }).catch((e) => console.error('レコード取得失敗',e));

// レコード登録処理
const params = {
app: 2,
record: {
  氏名: {
    value: resp.record.氏名.value,
  },
},
};
await kintone.api('/k/v1/record', 'POST', params).catch((e) => console.error('レコード登録失敗', e));
```

Promiseチェーンを活用してエラー処理を行うこともできるわけです。
try/catchでひとまとめにするパターンが楽でいいですけどね。

## 例外処理を発生させたいとき
kintoneカスタマイズでは`catch`に飛ぶだろう・・・と想定していることが`catch`に飛ばないことがあります。
それは次のようなケースです。
- `アプリ番号:1`の最新の顧客番号を持つレコードを取得する
- 取得した顧客番号に+1した値を顧客番号にセットして`アプリ番号:1`にレコードを登録する

この処理を行う時、もしレコードが一つもなかったら`catch`に飛ぶだろう・・・と思いませんか？
いいえ、飛びません。

レコードが一つもないのは、絞り込みで0件表示の時と同じ状態なのでエラーではないのです。
取得処理自体は成功している扱いになります。

こういう場合に使うのが`throw`です。例外を強制的に発生させることができます。

```JavaScript
try {
  // レコード取得処理
  const resp = await kintone.api('/k/v1/record', 'GET', {
    app: 283,
    query: 'order by 顧客番号 desc limit 1',
  });

  // レコードが０件の場合は例外を発生させる
  if (resp.records.length === 0) throw new Error('レコードが存在しません');

  // レコード登録処理
  const params = {
    app: 285,
    record: {
      顧客番号: {
        value: resp.records[0].顧客番号.value + 1,
      },
    },
  };
  await kintone.api('/k/v1/record', 'POST', params);
} catch (e) {
  console.error(e);
}

```

このように`throw`することで`catch`に強制的に飛ばすことができます。




## まとめ
- REST API と try/catchはセットで使いましょう
- 個別にエラー処理をしたい場合は await/catchを使いましょう
- 例外を発生させたいときは throw を使いましょう