@kintone/rest-api-client への移行を考える

# えっ！？ kintone JS SDK が・・・！！？

kintone の主戦場である JavaScript/TypeScript 界隈で kintone とのやりとりをカプセル化・抽象化するツールとして欠かせない存在である [kintone JS SDK](https://github.com/kintone/js-sdk)。

**なんといつの間にか deprecated になっていました。**

最終バージョンは 0.7.8 と、結局正式版リリースまで到達せず終了と言う事になりそうです。

旧 JS SDK の代替として 2020 年 2 月に [@kintone/rest-api-client](https://github.com/kintone/js-sdk/tree/master/packages/rest-api-client) 正式版がリリースされていました。
Git リポジトリのパスが妙に深いのが気になるところではありますが、執筆時点（2020 年 5 月下旬）では最新版が 1.3.0 まで上がって来ており、一般的な利用の範囲内では旧 JS SDK を置き換えられるものと判断して良いのではないかと思われます。

## kintone 開発キットの数奇な運命

思い起こせば kintone の開発キットは波乱含みであったように思います。
時系列で並べてみると、

| 時期         | 出来事                                                        |
| ------------ | ------------------------------------------------------------- |
| 2018 年 5 月 | kintone API SDK(β) for Node.js（kintone-nodejs-sdk）リリース  |
| 2019 年 3 月 | kintone JS SDK リリース                                       |
| 2020 年 2 月 | kintone JavaScript Client (@kintone/rest-api-client) リリース |

なんと！　初代、2 代目と、共に 1 年未満で次期ツールがリリースされると共に deprecated の憂き目を見ています！
短命で知られるジョースター家の男子だってもう少し長生きするよ！と言う感じです。

## @kintone/rest-api-client の特徴

新しい SDK、`@kintone/rest-api-client` はそれ自体が TypeScript で開発されており、旧 JS SDK が抱えていた（そして [kintone extension](https://qiita.com/iShinkai/items/49a3a4bfc863f2d92c98) でも解決できなかった）コード補完効かない問題を解決しています。
旧 JS SDK に `d.ts` ファイルが結局最後まで用意されなかった事を考えれば隔世の感がありますね。

旧 JS SDK では、確か 0.3 から 0.4 へのバージョンアップ時（0.4 から 0.5 の時だったかも）、メソッドに引数を渡す方式がそれまでの変数の羅列からオブジェクトを引き渡す形式に変わり数字以上の大きな影響がありましたが、@kintone/rest-api-client は初めからオブジェクトを引き渡す形になっています。
まあ昨今の潮流から言っても自然ですね。

他にも、同じ機能を受け持つメソッドの名前が変わったり、戻り値の形が変わっていたりと、基本的には「そりゃこうあるべきだろ」と言うポジティブな方向にブラッシュアップされており、旧 JS SDK が抱えていた不自然さがだいぶ改善されています。

と言う事で、この記事では 3 代目ジョジョ、もとい 3 代目 SDK である @kintone/rest-api-client について、**旧 JS SDK からの移行**と言う観点で**各種レコード操作**の実際のコードの違いを含めて見ていきたいと思います。

# @kintone/rest-api-client を試す

以下の解説では、[他の記事](https://qiita.com/iShinkai/items/f832ffd0436a3630832e) でも解説している kintone 側で用意している雛形アプリの 1 つ「**案件管理**」アプリを利用しています。
以下のような下処理をやっています。

- アプリを作成時、「サンプルデータを含める」で作成
- 各フィールドのフィールドコードをフィールド名と合わせておく
- プロジェクトは [kintone-cli](https://github.com/kintone/kintone-cli) で作成
- 作成したプロジェクトを開き`@kintone/rest-api-client` をインストール

`kintone-cli` についてもいつか記事を書きたいと思っています。

さて、プロジェクトの準備ができたら早速動作を見てみます。
良く使うメソッド類を確かめてみましょう。

なお、確認のために使用したコードは [GitHub](https://github.com/iShinkai/kintone-sdk-test) にアップしています。

## レコード 1 件取得・レコード複数取得

`getRecord()` 及び `getRecords()` に関しては 旧 JS SDK との間でリクエスト・レスポンスの違いはありませんでした。
クエリの指定の仕方も変わりません。

```typescript:@kintone/rest-api-clientのgetRecords()
const result = await new KintoneRestAPIClient().record.getRecords({
  app: appId,
  fields: ["レコード番号", "確度", "会社名"],
  query: '確度 in ("A") order by レコード番号 asc',
});
```

```json
{
  "records": [
    // 省略
  ],
  "totalCount": null
}
```

## レコード一括取得

レコード一括取得はメソッド名が違います。
旧 JS SDK では `getAllRecordsByQuery()`, `getAllRecordsByCursor()` と言うメソッド名でしたが、@kintone/rest-api-client では目的に応じて `getAllRecords()`, `getAllRecordsWithId()`, `getAllRecordsWithOffset()`, `getAllRecordsWithCursor()` と 4 つに分かれています。
これらのメソッドは旧 JS SDK とは渡す引数の構成も違って来ており、単純にメソッド名を一括置換するだけでは移行できないので注意が必要です。

```typescript:旧JSSDKのgetAllRecordsByQuery()
const result = await new kintoneJSSDK.Record().getAllRecordsByQuery({
  app: appId,
  fields: ["レコード番号", "確度", "会社名"],
  query: '確度 in ("A") order by レコード番号 asc',
});
```

```typescript:@kintone/rest-api-clientのgetAllRecords()
const result = await new KintoneRestAPIClient().record.getAllRecords({
  app: appId,
  fields: ["レコード番号", "確度", "会社名"],
  condition: '確度 in ("A")',
  orderBy: "レコード番号 asc",
});
```

`query` の部分が `condition` に変わっており、`order by` は `orderBy` パラメータに切り出す必要があるのが見て取れますね。

ただ、`getAllRecordsWithCursor()` メソッドは、

```typescript:@kintone/rest-api-clientのgetAllRecordsWithCursor()
const result = await new KintoneRestAPIClient().record.getAllRecordsWithCursor({
  app: appId,
  fields: ["レコード番号", "確度", "会社名"],
  query: '確度 in ("A") order by レコード番号 asc',
});
```

と、旧 JS SDK の `getAllRecordsByCursor()` と同じような書き方が出来ます。

レスポンスに関しては、旧 JS SDK が

```json:旧JSSDKのレスポンス
{
  "records": [
    // 省略
  ],
  "totalCount": 10
}
```

と言う形で返って来るのに対し、@kintone/rest-api-client では

```json:@kintone/rest-api-clientのレスポンス
[
  // 省略
]
```

と、ちょうど `records[]` の中身に相当するものだけが返却されます。

従って、旧 JS SDK で `getAllRecordsBy****()` を使用していた部分は、それなりに慎重に書き換えが必要になります。

## レコード 1 件登録

`addRecord()` メソッドは、両 SDK とも変わりはありません。
レスポンスの形式も同じです。

## レコード複数登録

`addRecords()` メソッドも呼び出し方は両 SDK とも変わりはありません。
100 件までの同時登録が可能と言う仕様も変わりません。

ただし、レスポンスが若干異なります。
@kintone/rest-api-client では以下のように

```json:@kintone/rest-api-clientのレスポンス
{
  ids: [ （省略） ],
  records: [ { id: 'xx', revision: 'xxx' }, ... ],
  revisions: [ （省略） ],
}
```

と、旧 JS SDK にはなかった `records[]` が含まれるようになっています。
情報としては重複していますが、使い勝手は良くなっていると言えるでしょう。

## レコード一括登録

`addAllRecords()` は、リクエストの形式は変わらず、100 件以上のレコード登録時に利用すると言う目的も同じです。

一方、レスポンスは大きく異なります。

旧 JS SDK では

```json:旧JSSDKのレスポンス
{
  "results": [
    {
      "ids": [ （省略） ],
      "revisions": [ （省略） ]
    },
    ...（省略）
  ]
}
```

となっていたのに対し、@kintone/rest-api-client では

```json:@kintone/rest-api-clientのレスポンス
{
  "records": [{ "id": "xx", "revision": "xxx" }, ...（省略）]
}
```

となっています。
`addRecords()` の `records[]` のみが取り出された格好です。

`addAllRecords()` は内部的には [`bulkRequest API`](https://developer.cybozu.io/hc/ja/articles/201941814) をコールしており、もともとこの API の目的が複数アプリのレコード一括操作であることから、旧 JS SDK では `results[]` と言う配列に格納されて結果が返される仕様だったわけです。
しかしながら正直なところ SDK のメソッドを利用する側からすれば無駄に複雑で意味不明に見えるレスポンス形式であったのは否めません。
（`addAllRecords()` メソッドは複数アプリに対してどうこうと言う操作ができるわけではないので）
@kintone/rest-api-client では、この不合理を解決し、あるべき形でレスポンスを戻すようになりました。
非常に使いやすくなったと言える一方、旧 JS SDK からの移行に関しては注意を要する箇所です。

## レコード 1 件更新

旧 JS SDK では `updateRecordById()` あるいは `updateRecordByUpdateKey()` と名前が分かれていましたが、@kintone/rest-api-client では `updateRecord()` に一本化されました。
パラメータとして渡した値（ `id` か `updateKey` か）でどちらをキーにするかが決まる形です。
なお `id` と `updateKey`同時に指定すると `400` が返って来ます。

レスポンスの形式は同じです。

## レコード複数更新

`updateRecords()` は引数の形もレスポンスの内容も新旧 SDK 間で違いはありません。

なお、@kintone/rest-api-client の `updateRecords()` 等いくつかのメソッドでは、コード補完で表示されるポップアップの内容に誤りが見られます。
<img alt="resized-mod-01-updateRecords.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/162138/46152a91-680c-24f5-0e6e-a897717e99b9.png">
これってどこ由来？と思って [GitHub 上の当該ソース](https://github.com/kintone/js-sdk/blob/master/packages/rest-api-client/src/client/RecordClient.ts) や `node_modules` の `d.ts` ファイル等を確かめてみたのですが、それらでは本来あるべき値になっている。
どういう理由でここがおかしくなっているのか不明です。
VS Code 側のバグ？筆者の環境だけかしら？

## レコード一括更新

`updateAllRecords()` は、メソッド名や与える引数の形は同じですが、上述の [レコード一括登録](#レコード一括登録)と同様、レスポンスの形が大きく異なります。

旧 JS SDK では

```json:旧JSSDKのレスポンス
{
  "results": [
    {
      "records": [{ "id": "xx", "revision": "xxx" }, ...（省略）]
    },
    ...（省略）
  ]
}
```

ですが、@kintone/rest-api-client では

```json:@kintone/rest-api-clientのレスポンス
{
  "records": [{ "id": "xx", "revision": "xxx" }, ...（省略）]
}
```

となります。
旧 JS SDK の `results[0]` が取り出されて返って来ている格好です。

## レコード複数削除

`deleteRecords()` は両 SDK とも変わりはありません。
レスポンスの形式も同じで、全レコード正常削除できれば `{}` が返ります。

なお、@kintone/rest-api-client の `deleteRecords()` や次に触れる `deleteAllRecords()` は `updateRecords()` 等と同様コード補完に誤りがあるので注意です。

## レコード一括削除

レコード一括削除は両 SDK 間でコンセプトが大きく異なっています。
旧 JS SDK の `deleteAllRecordsByQuery()` は、

```typescript:旧JSSDKのdeleteAllRecordsByQuery()
const result = await new kintoneJSSDK.Record().deleteAllRecordsByQuery({
  app: appId,
  query: '確度 in ("A")',
});
```

と言った具合に、その名の通りクエリで削除対象とする条件を指定する事ができました。
一方 @kintone/rest-api-client では `deleteAllRecords()` と言うメソッド名で、

```typescript:@kintone/rest-api-clientのdeleteAllRecords()
const result = await new KintoneRestAPIClient().record.deleteAllRecords({
  app: appId,
  records: [{ id: "1" }, { id: "2" }, { id: "3" }],
});
```

と言った形であくまで ID （とリビジョン）で構成されるオブジェクトの配列で対象を指定する形になります。
@kintone/rest-api-client にはクエリで削除対象を抽出して削除、と言う機能を持つメソッドは実装されていません。

これは恐らく、削除対象をクエリで指定すると言うのは kintone 側に対象抽出を丸投げしてしまう事になり、しかもレスポンスにはどのレコードを削除したと言う明確な結果が返って来ない点を不合理だと判断したものと思われます。
そりゃ「削除したいなら削除したいものを明示してくれよ」と要求するのは当然ですよね。
で、その @kintone/rest-api-client で指定する `records` ですが、これは `addRecords()` 等で返却される `records` オブジェクトと同じ形式で、その意味でも @kintone/rest-api-client の考え方には一貫性があります。

戻り値も形式が異なります。

旧 JS SDK では

```json:旧JSSDKのレスポンス
{
  "results": [{}]
}
```

ですが、@kintone/rest-api-client では

```json:@kintone/rest-api-clientのレスポンス
{}
```

です。

# まとめ

各メソッドについて、単純に置き換え可能か、修正が必要かどうかをまとめると以下のようになります。

| 処理内容                     | kintone JS SDK                                  | @kintone/rest-api-client  | 移行時のコード修正                                       |
| ---------------------------- | ----------------------------------------------- | ------------------------- | -------------------------------------------------------- |
| レコード 1 件取得            | getRecord()                                     | getRecord()               | 不要                                                     |
| レコード複数取得             | getRecords()                                    | getRecords()              | 不要                                                     |
| レコード一括取得<br>（クエリ）   | getAllRecordsByQuery()                          | getAllRecords()           | かなり修正が必要<br>（渡す引数と戻り値の形式が異なる）   |
| レコード一括取得<br>（カーソル） | getAllRecordsByCursor()                         | getAllRecordsWithCursor() | 修正が必要<br>（戻り値の形式が異なる）                   |
| レコード 1 件登録            | addRecord()                                     | addRecord()               | 不要                                                     |
| レコード複数登録             | addRecords()                                    | addRecords()              | 不要                                                     |
| レコード一括登録             | addAllRecords()                                 | addAllRecords()           | 修正が必要<br>（戻り値の形式が異なる）                   |
| レコード 1 件更新            | updateRecordById()<br>updateRecordByUpdateKey() | updateRecord()            | 微修正が必要<br>（メソッド名が異なる）                   |
| レコード複数更新             | updateRecords()                                 | updateRecords()           | 不要                                                     |
| レコード一括更新             | addAllRecords()                                 | addAllRecords()           | 修正が必要<br>（戻り値の形式が異なる）                   |
| レコード複数削除             | deleteRecords()                                 | deleteRecords()           | 不要                                                     |
| レコード一括削除             | deleteAllRecordsByQuery()                       | deleteAllRecords()        | かなり修正が必要<br>（メソッドのコンセプト自体が異なる） |

上述の「不要」の部分にしても、

```typescript:旧JSSDK
const result = await new kintoneJSSDK.Record().getRecords({
  app: appId,
});
```

を

```typescript:@kintone/rest-api-client
const result = await new KintoneRestAPIClient().record.getRecords({
  app: appId,
});
```

と書き換える程度の修正は当然必要ですのでご注意ください。

# 感想

と言う事で、新たにリリースされたクライアントツールであるところの **@kintone/rest-api-client** についてざっと見て来ました。
もちろん他にもカーソルだったりコメントであったりの操作もあるので、これで全部と言うわけではありません。異常系のパターンも見てませんしね。

最初の方でも述べましたが、 @kintone/rest-api-client は現時点で既に旧 JS SDK の大部分の機能をカバーしており、新規プロジェクトにおいては旧 JS SDK ではなくこちらをファーストチョイスとして問題ないクォリティでしょう。
一方、既に旧 JS SDK を導入済みのプロジェクトでは、メソッドの名前や引数、レスポンスの形式がそれなりに異なることから、安易な移行（置き換え）には慎重さが求められそうです。

初めの方に見た通り、kintone の SDK は 2 代連続で 1 年程度で deprecated の憂き目を見ており、当代であるところの 3 代目が来年の夏を無事に迎えられるかは今は判りません・・・が、さすがにそろそろこれが決定版！と言えるようなものになって欲しいなと思います。

kintone は開発者の環境を良くするために様々なツールをリリースする事に非常に積極的で、そのおかげで [開発者ドキュメント](https://developer.cybozu.io/hc/ja/articles/360028177472) を上から下まで全部読み込まなければカスタマイズなんて碌にできません、みたいな時代は既に過ぎ去ったと言えるでしょう。
ただその分、それぞれ個々のツールは若干機能不足が散見されたり、メンテナンスが止まったり、今回のように短期間で代替ライブラリへの移行が発生したりと、どことなくこれらのプロジェクトに関わる全体としてリソースが不足気味なのかなあと感じさせる場面も。
そんなん言うならお前がコントリビューターとして貢献しろよ。って話かも知れませんがね。

と言うわけで **@kintone/rest-api-client** のざっくりレビューでした！
どちら様も良き kintone ライフを！

# 参考

[kintone JavaScript Client (@kintone/rest-api-client)](https://developer.cybozu.io/hc/ja/articles/900000767263)