数年前から、社内の業務システムをオンデマンドで構築する際に Web データベースサービスの Canbus. を利用しています。
データ量に応じた課金で利用ユーザー数の自由度が高いことに加えて、簡単な Web API もついているので重宝しているのですが、この API に少し癖があるため、使用する環境によっては注意が必要な部分があります。
Google Workspace で利用可能な Google Apps Script (GAS) から Canbus. API を呼び出す方法と、注意点についてまとめました。
Canbus. について
Canbus. は、Web データベースやいわゆる「ノーコード」と呼ばれる製品群に属するサービスで、月額利用が可能な SaaS です。
定型的な業務アプリを簡単に組めることはもちろんなのですが、他の類似サービスと違うと感じるのは、少し複雑な業務ロジック(複数アプリの非同期での連動、バッチ処理、スケジュール処理など)を組む際にも JavaScript のようなスクリプトで実装する必要がなく、ビルトインの機能でカバーできる領域が広いと感じる点です。
また、課金体系にも特徴があり、登録ユーザー数で課金されるのではなく、データ量に応じた課金になっています。具体的には、自社アカウント内で登録可能なレコード数に上限があり、それを超過する場合にはオプション料金をトップアップするという仕組みです。これが実際に適しているかどうかは、利用スタイル次第だといえますが、「社員全員が毎日必ず使うわけではないが、全員が使えるようにはしておきたいアプリ」や、「社外の不特定数のユーザーにアカウントを発行して使ってもらう必要があるアプリ」を依頼された時に、ライセンス数を気にせずに組める点はメリットだと感じます。(逆に、少数の固定ユーザーが多数のレコードを登録するような用途であれば、ユーザー課金のサービスの方が向いているといえます)
Canbus. で提供されている Web API
Canbus. では、ユーザーに利用してもらう入力フォームを Web 画面上で組み、ブラウザを通じてデータの登録・閲覧を行うのですが、これ以外に Web API を使用してデータを操作することもできるようになっています。
現時点で、提供されている Web API は以下の 6 種類です。
- レコード作成 API
- レコード編集 API
- レコード削除 API
- レコード一覧 API
- レコード取得 API
- メタデータ API
6 番目のメタデータ API のみ、多少分かりにくいですが、これは Canbus. のアプリ内にある各テーブルの設定情報(各テーブルにどのようなデータ項目が配置されているか)を取得するための API です。メタデータ API 以外は、名前のままであり、これらを使用して Canbus. 内のレコードの CRUD ができます。
一定の呼び出しレート制限はあるものの、Canbus. 内のデータをほぼ制約なく読み書き可能な API が提供されているので、Canbus. の入力フォームをフロントエンドとして使いつつ、蓄積されたデータを外部システムと連携させて使うことがかなり自由に実現できます。
Google Apps Script から Canbus. API を呼び出す
前置きが長くなりましたが、実際に Canbus. の API を Google Workspace に付属する Google Apps Script (GAS) から呼び出してみます。(個人アカウントの Google ドライブで作成可能な GAS でも手順は同じです)
GAS を使用すると、Google Workspace 内のデータと外部システムのデータとを連携させる処理をサーバーレスで書けるため、小回りのきく業務システムを追加コストなしで簡単に作るのに適しているといえます。
Canbus. API に限らないことですが、GAS から外部の Web API を呼び出す際には、 HTTP リクエストを送信するための UrlFetchApp を使用します。
const response = UrlFetchApp.fetch('https://<tenant-id>.canbus.com/api/records', {
method: 'post',
headers: {
'Content-Type': 'application/json',
},
payload: JSON.stringify({
tenant_id: '<tenant-id>',
app_id: 'f898b8bca13041b2b860f471',
api_key: 'e1Pwg9EgbobAhoze',
api_secret: '5N4cOMC5z08T49Y36yTLRpU4xhyyta3H',
add: [{
Text: 'Code and Life',
Number: 1234567890,
System_Create_User: 'admin'
}],
}),
});
console.log(response.getContentText());
これは Canbus. のレコード作成 API を呼び出す最小レベルのコードです。アプリ ID や API キーなどは架空の値にしてあります。
- Canbus. では、アクセスする URL のホスト名部分に自社固有の ID が入ります。API 呼び出し時にもこの URL は踏襲されるので、上記コードの <tenant-id> 部分は自社固有の ID で置き換える必要があります。
- レコード作成 API の場合、HTTP メソッドは POST です。
- Content-Type ヘッダーの値として "application/json" を送信します。これは省略できません。
- リクエストボディ(API 呼び出しのペイロード)は JSON 形式の文字列になります。この中に、API 呼び出しの認証情報と、作成するレコードの情報を両方とも含めます。
認証情報を API のペイロードに含めてリクエストする点は、Canbus. API の特徴的な仕様だと言えると思います。認証トークンを取得するための API を前処理として呼び出したり、Authorization ヘッダーに含めて認証情報を渡すのではなく、事前に Canbus. のテーブル設定画面で発行した API キーとシークレットをそのまま、上記のように JSON 文字列の一部として渡す必要があります。
上記のサンプルコードでは、console.log() を使用して API レスポンスを出力しています。認証情報や URL に間違いがなければ、レコード作成が成功して、以下のようにレスポンスが出力されます。
{"authen":"OK","results":[{"result":"0","id":"d034973c6bc64d18b61719dd"}]}
表示されている ID が、Canbus. のレコード ID と呼ばれるものです。API の詳しいリクエストとレスポンスの仕様は、レコード作成 API のリファレンスを参照してください。
API 呼び出し後に Canbus. 上のテーブルを確認すると、文字列項目 = "Code and Life", 数値項目 = 1234567890 と入力されたレコードが作成されています。
Google Apps Script で呼べない Canbus. API
レコード作成 API に続いて、レコード一覧 API を使ってみます。これは、アプリに含まれるレコードを一覧で取得できる API です。
UrlFetchApp.fetch('https://<tenant-id>.canbus.com/api/records', {
method: 'get',
headers: {
'Content-Type': 'application/json',
},
payload: JSON.stringify({
tenant_id: '<tenant-id>',
app_id: 'f898b8bca13041b2b860f471',
api_key: 'e1Pwg9EgbobAhoze',
api_secret: '5N4cOMC5z08T49Y36yTLRpU4xhyyta3H',
record_list: {
},
}),
});
リファレンスにも書かれている一番シンプルなレコード一覧 API 呼び出しになっているはずなのですが、実際にはこの API 呼び出しは失敗してしまいます。
UrlFetchApp.fetch() メソッドのオプションに muteHttpExceptions: true を指定して HTTP ステータス コードとレスポンスを調べてみると、400 Bad Request が返され、"authen": "NG" となっています。
400: {"authen":"NG"}
最初、なぜこのようなエラーになるのか理解できなかったのですが、原因は GAS の UrlFetchApp.fetch() メソッドが HTTP GET 時にはペイロード、つまり fetch() メソッドの第 2 引数で指定している payload の値を送信しない仕様になっている点にあります。これは、GAS のリファレンスにもはっきりと書かれています。
Canbus. API では、HTTP GET を使用するレコード一覧 API, レコード取得 API, メタデータ API でも、仕様上ペイロードの送信が必須です。一方で GAS は HTTP GET でペイロードを送信しない仕様になっているので、これらの API は GAS からは呼べないということになります。
(なお、HTTP GET の使用とペイロードの送信が原理的に両立しないわけではありません。RFC の解釈は別として、例えば Postman や cURL のように、HTTP GET メソッド使用時でもペイロードを送信可能な実装は多数あります)
HTTP メソッドの上書き
GAS からレコード一覧 API やレコード取得 API が呼べないのであれば、実用上 Canbus. API は使いものにならなくなってしまうのですが、幸い Canbus. API 側に回避手段が用意されています。
具体的には、まず POST メソッドで Canbus. API を呼び出すようにコードを変更します。これにより、GAS がペイロードを送信するようになります。それと合わせて、リクエストヘッダー X-Http-Method-Override またはクエリパラメーター x-http-method-override を使用して、本来の API 呼び出しメソッドが POST ではなく GET であることを宣言します。
const response = UrlFetchApp.fetch('https://<tenant-id>.canbus.com/api/records', {
method: 'post',
headers: {
'Content-Type': 'application/json',
'X-Http-Method-Override': 'get',
},
payload: JSON.stringify({
tenant_id: '<tenant-id>',
app_id: 'f898b8bca13041b2b860f471',
api_key: 'e1Pwg9EgbobAhoze',
api_secret: '5N4cOMC5z08T49Y36yTLRpU4xhyyta3H',
record_list: {
},
}),
});
console.log(JSON.stringify(JSON.parse(response.getContentText()), null, 4));
上記のサンプルコードでは、method: "post" として使用する HTTP メソッドを POST に変更した上で、リクエストヘッダーを表す headers オブジェクトのプロパティとして 'X-Http-Method-Override': 'get' を指定しています。
このように修正すると、API 呼び出しが成功するようになり、コンソール出力として以下のようにレコード一覧 API のレスポンスが得られます。JSON.stringify() で整形しています。
{
"authen": "OK",
"results": [
{
"result": "0",
"count": 1,
"length": 1,
"records": [
{
"_id": "d034973c6bc64d18b61719dd",
"Text": "Code and Life",
"Number": 1234567890,
"System_Create_User": "admin",
"System_Create_Time": "2024/05/30 12:34",
"System_Update_User": "admin",
"System_Update_Time": "2024/05/30 12:34"
}
]
}
]
}
まとめ
Web データベース製品 Canbus. の Web API を Google Apps Script (GAS) から呼び出す場合、リクエストの送信には UrlFetchApp.fetch() を使用します。
Canbus. API のリクエストでは常にペイロードの送信が必要です。このため、GAS でペイロードが送信されない HTTP GET を必要とする API を使用する場合は、リクエストヘッダー X-Http-Method-Override またはクエリパラメーター x-http-method-override を使用して HTTP メソッドの上書きを行います。
GAS を使用して Canbus. のレコードデータを CRUD することで、Canbus. の入力フォームや各種機能を業務システムのフロントエンドとして利用しつつ、Google Workspace や既存の社内システムとの間でデータを連携させ、活用することができるといえます。