kintoneユーザーの僕が「不便すぎる」を副業で解決してみた
どうも、拓海です。
今日はちょっと個人的な話から始めさせてほしい。
僕は本業でkintoneをバリバリ使っている。営業管理、案件進捗、日報、見積書の管理。会社の業務の大半がkintoneで回っている。
kintoneって、本当によくできたツールだと思う。ノーコードでアプリを作れて、レコード管理が簡単で、ワークフローも組める。会社に導入されたときは「これは便利だ」と素直に感動した。
でも、使えば使うほど「ここ、なんでこんな不便なんだろう」と感じる場面が出てくる。
特にずっと引っかかっていたのが、スプレッドシートとの連携だ。
kintoneユーザーの「あるある不満」
こういう場面、kintoneを使っている人なら絶対に経験があると思う。
取引先のリストをExcelかスプレッドシートで管理していて、それをkintoneに移行しようとするとき。あるいは、kintoneに溜まったデータを加工・分析しようとして、スプレッドシートに書き出そうとするとき。
kintoneにはCSVインポート・エクスポートの機能がある。だから一応できる。でも手順がいちいち面倒くさい。
kintoneにデータを入れるとき
スプレッドシートをCSVでダウンロード
kintoneのインポート画面を開く
CSVをアップロード
列のマッピングを確認
インポート実行
エラーがあれば修正して再実行
kintoneからデータを出すとき
kintoneで一覧を開く
CSVエクスポートを実行
スプレッドシートにインポート
ヘッダーが日本語で汚いので整形
ようやく分析できる
月に1回ならまあ我慢できる。でも週次でやるとなると、この手順だけで毎回30分近く消える。年間で計算すると20時間以上。これ、完全に無駄だと思っていた。
しかも一番腹が立つのが、「少しだけ修正して戻す」という作業だ。
kintoneのデータをスプレッドシートに出して、10件だけ内容を修正して、またkintoneに入れ直す。この場合、CSVを使うとデータが全部上書きされてしまう可能性があって怖い。かといって1件ずつkintoneの画面で手打ちするのも時間がかかる。
「なんかいい方法ないかな」と思いながら、ずっとモヤモヤしていた。
副業でkintone開発を始めた理由
本業でkintoneを使いながら、僕は副業でkintoneカスタマイズの開発もやっている。
最初は「JavaScriptの勉強がてら」くらいの軽い気持ちだった。kintoneはJavaScriptでカスタマイズできる。業務で毎日触っているツールだから、仕様も理解しやすい。これは相性がいいなと思って、Coconalaに出品を始めた。
やっていく中で気づいたのが、「本業でkintoneを使っているからこそ見える課題がある」ということだった。
外部の開発者は、クライアントから要件を聞いてツールを作る。でも僕は、日常的にkintoneを使うユーザーとして「これが不便だ」という感覚を持っている。その不満が、そのまま開発のアイデアになる。
ユーザー目線で作ったものは、同じくkintoneを使っている人に刺さる。これが僕の副業の軸になった。
で、ずっと感じていた「スプレッドシートとの連携が面倒くさい」という不満を、いよいよ自分で解決しようと思い立った。
最初の構想:「スプレッドシートからkintoneへ登録するだけ」
最初に考えた仕様は、シンプルだった。
スプレッドシートに入力したデータを、ボタン1つでkintoneに一括登録する。
それだけ。
kintoneのREST APIを使えば、GAS(Google Apps Script)から直接POSTリクエストが投げられる。スプレッドシートの各行をkintoneのレコードとして登録する。これは技術的には難しくない。
でも一つ問題があった。重複登録だ。
スプレッドシートのデータをkintoneに登録するとき、すでに登録済みのデータまで二重に入れてしまうリスクがある。「前回実行したときのデータと、今回追加したデータが混在している」というのはよくある状況だ。
だから「重複チェック付き」を最初から仕様に入れた。登録前にkintone側の既存レコードと照合して、すでにあるものはスキップする。
スプレッドシートのデータ
↓
kintoneの既存レコードと照合
↓
未登録のものだけPOST
↓
結果を「登録結果」列に書き込み
これでv1.0が完成した。動いた。テストも通った。「よし、出品しよう」と思った。
コードレビューで発覚した「時限爆弾」
出品前に、最新のkintone APIの仕様を確認しながらコードをレビューした。
そこで発覚した問題が二つあった。
一つ目:offsetの10,000件上限
重複チェックのためにkintoneから既存レコードを全件取得する処理で、こういうコードを書いていた。
while (true) {
const query = `... offset ${offset}`;
offset += 500;
}
kintoneのAPIには「offsetで指定できる上限は10,000件まで」という制限がある。つまり、レコードが1万件を超えた瞬間にこのコードは壊れる。
本業でkintoneを使っている僕は知っていた。データは確実に積み上がっていく。最初は数百件でも、2年後には余裕で1万件を超える。「時限爆弾」そのものだった。
解決策は「$idシーク法」。offsetを使わず、前回取得した最後のレコードIDを起点に次の500件を取り続ける方式だ。これでどれだけレコードが増えても対応できる。
二つ目:GASの6分タイムアウト
GASには実行時間の上限がある。1回約6分。登録件数が多いと途中で切断される。どこまで処理したか分からなくなるのは最悪だ。
PropertiesServiceを使って進捗を保存し、タイムアウトになっても続きから再開できる仕組みを実装した。
この二つを修正してv2.0を完成させたとき、「これで出品できる」と思った。でもそこで、もっと大事なことに気づいた。
「APIトークンをコードに書くのはまずい」
v2.0のコードには、こう書いてあった。
const CONFIG = {
SUBDOMAIN: "your-subdomain",
APP_ID: 123,
API_TOKEN: "your-api-token-here",
};
Coconalaで販売するということは、このコードをお客様に渡すということだ。コードをそのまま誰かに見せれば、APIトークンが漏洩する。
本業でkintoneを管理している立場として、これがどれだけまずいか分かる。APIトークンが漏れれば、kintoneのデータに外部からアクセスできてしまう。セキュリティ事故になりかねない。
解決策は「スクリプトプロパティへの分離」だった。GASにはコードとは別に値を保存できる仕組みがある。APIトークンはそこに入れて、コードからは実行時に読み込むだけにする。
function getCredentials() {
const props = PropertiesService.getScriptProperties();
return {
subdomain: props.getProperty("KINTONE_SUBDOMAIN"),
appId: Number(props.getProperty("KINTONE_APP_ID")),
apiToken: props.getProperty("KINTONE_API_TOKEN"),
};
}
これでコード上にAPIトークンは一切存在しない。納品物のコードを誰に見せても、接続情報は漏れない。
さらに「初期設定ウィザード」も追加した。スプレッドシートのメニューから実行すると、ダイアログが順番に表示されてサブドメイン・アプリID・APIトークンを入力できる。技術に詳しくないお客様でも迷わずセットアップできる設計だ。
これでv2.1が完成した。
ここで仕様が大きく変わった
v2.1を完成させて、もう一度自分でテスト運用してみた。
本業のkintoneデータを実際に使って動かしてみると、「あ、やっぱり足りない」と気づいた。
スプレッドシートからkintoneへの登録はできる。でも、kintoneのデータをスプレッドシートに出してきて、それを編集して、またkintoneに戻すという操作ができない。
ずっと感じていたあの不満、「少しだけ修正して戻したい」という課題が、まだ解決されていなかった。
これを解決するには、双方向同期が必要だ。
kintone → スプレッドシート(PULL)
スプレッドシート → kintone(PUSH)
この両方向が一つのツールで完結する仕様にすれば、僕が本業で感じていた不満を根本から解決できる。
「あ、これ仕様変更しよう」と即決した。
$idによる自動判定という設計の妙
双方向同期を実装するとき、一番悩んだのが「新規登録と更新をどう区別するか」という問題だった。
スプレッドシート上に100行のデータがある。そのうち80行はkintoneから引っ張ってきた既存データで、20行は新しく追加したデータだ。PUSHを実行したとき、既存の80行は「更新(PUT)」、新規の20行は「新規登録(POST)」として処理しないといけない。
お客様に「この行は更新です」「この行は新規です」と手動で選ばせるのは面倒くさい。それはツールとして失格だ。
答えはシンプルだった。kintoneのレコード番号($id)を使う。
PULLでkintoneからデータを取得するとき、各レコードの$idも一緒に取得してシートのA列に入れる。新しく追加した行にはA列が空欄になる。PUSHのとき、A列に値があれば「更新」、空欄なら「新規登録」と自動判定する。
if (idVal === "") {
// $idなし → 新規POST
} else {
// $idあり → 更新PUT
}
お客様がやることは、シートを見ながらデータを編集して、新しい行を追加するだけ。$idの列は触らなくていい。ツールが勝手に判断してくれる。
本業でkintoneを使ってきた経験から「ユーザーが意識しなくていい設計」がどれだけ大事かを知っていたから、ここは絶対に妥協しなかった。
フィールドコードの問題も解決した
もう一つ、本業ユーザーとして「これ毎回面倒だな」と思っていたことがある。
kintoneのフィールドコードの確認だ。
kintoneのフィールドコードは、アプリ設定画面でフィールドをひとつひとつクリックして確認しないといけない。フィールドが10個あれば10回クリック。しかも確認した値を手動でコードに入力する。これが地味にストレスだった。
調べたら、kintoneのAPIで一発取得できることが分かった。
GET /k/v1/app/form/fields.json
このエンドポイントで全フィールドの情報が返ってくる。これをGASでスプレッドシートに書き出す機能を追加した。メニューから「フィールドコード一覧を取得」を実行するだけで、専用シートに全フィールドが一覧表示される。
さらに進めて、このシートをそのまま「設定ファイル」として使う仕組みにした。D列に「✅」が入っているフィールドだけを登録・更新の対象にする。✅を外せば除外される。コードを一切触らずに、設定が完結する。
⚙️ kintone設定シート
┌─────────────┬──────────────┬────────────────┬──────┐
│ 列名 │ フィールドコード│ 種別 │ 対象 │
├─────────────┼──────────────┼────────────────┼──────┤
│ 会社名 │ 文字列__1行_ │ SINGLE_LINE_TEXT│ ✅ │
│ 見積金額 │ 数値 │ NUMBER │ ✅ │
│ 受注予定日 │ 日付 │ DATE │ ✅ │
│ 更新者 │ 更新者 │ MODIFIER │ │ ← 除外
└─────────────┴──────────────┴────────────────┴──────┘
技術に詳しくないkintoneユーザーでも、直感的に操作できる。これは本業ユーザーの視点がなければ出てこないアイデアだと思っている。
操作パネルを作ったら、製品感が出た
最後に「ボタンで操作できないか」という話になって、操作パネルを実装した。
GASにはキーボードショートカットを直接登録する機能がないので、スプレッドシート上に「🎮 操作パネル」という独立したシートを作って、そこにボタンを配置した。
設計上の工夫として、データシートにボタンを置かなかった。PULLを実行するとデータシートがリフレッシュされるので、そこにボタンを置くと消えてしまうリスクがある。だから専用の操作パネルシートを分けて、ここだけは絶対に触らない領域にした。
🎮 操作パネル ← ボタン6個(触らない)
⚙️ kintone設定 ← ✅で設定するだけ
kintoneデータ ← kintoneのデータが展開される
createControlPanel()という関数を1回実行するだけで、パネルが自動生成される。コード納品の形を保ちながら、ボタン操作という使いやすい体験を提供できるようになった。
このパネルが完成したとき、「あ、ちゃんとしたプロダクトになったな」と感じた。
完成したv3.0でできること
最終的にできあがったツールを改めて整理するとこうなる。
PULL(kintone → スプレッドシート)
kintoneの全レコードをスプレッドシートに一括展開
レコード番号($id)をA列に自動付与
初回のみ列幅を自動調整(2回目以降はユーザー設定を維持)
PUSH(スプレッドシート → kintone)
$idあり → 更新(PUT)を自動実行
$idなし → 新規登録(POST)を自動実行
結果(登録済/更新済/エラー)を操作結果列に自動書き込み
安全性
APIトークンはスクリプトプロパティで管理(コード上に記載なし)
10,000件超のレコードも$idシーク法で安全に取得
GAS6分タイムアウト対策(自動中断・再開機能)
使いやすさ
初期設定ウィザード(ダイアログ入力3回でセットアップ完了)
フィールドコード一覧を自動取得・設定シートに展開
✅を選ぶだけのFIELD_MAPPING設定(コード修正不要)
ボタン付き操作パネルシート(コードのメニューより直感的)
最初の「スプレッドシートから登録するだけ」という構想から比べると、できることが5倍以上になった。
kintoneユーザーだからこそ作れたツール
このツールを作り終えて改めて思うのは、「ユーザーとして使ってきた経験が全部活きた」ということだ。
offsetの上限問題は、本業でkintoneのデータが蓄積していく様子を見てきたから「将来必ず問題になる」と分かった。
APIトークンの管理は、本業でkintoneの管理者もやっているから、セキュリティリスクの重大さを肌で知っていた。
$idによる自動判定は、「ユーザーに余計な操作をさせたくない」という感覚が、kintoneを実際に使っているからこそ強くあった。
フィールドコードの一覧取得は、自分が毎回「面倒だな」と思っていたから出てきたアイデアだ。
外部の開発者がヒアリングで引き出せる要件は、お客様が言語化できているものだけだ。でも「なんとなく不便」「毎回ちょっとストレス」という感覚は、自分が使っていないと分からない。
副業でkintone開発をやるなら、本業でkintoneを使っているというのは、明確な強みになる。
次回予告
今回作ったツールはCoconalaで近日出品予定だ。
ただ、まだ終わっていない。
このツールの次のバージョンでは、スプレッドシート上で操作した内容を、リアルタイムに近い形でkintoneに反映させる仕組みを作ろうと考えている。
GASにはトリガー機能があって、「スプレッドシートが編集されたとき」「指定した時間になったとき」に自動でスクリプトを実行できる。これを使えば、スプレッドシートを編集するだけで自動的にkintoneが更新される、という体験が作れる。
「スプレッドシートを触っていたら、kintoneも自動で最新になっている」。
本業でずっと感じていた不満の、最終回答がそこにある気がしている。
次回もnoteに書くので、ぜひフォローしておいてほしい。
ではまた。
拓海(Takumi)
