Cloudflare R2を触った後はD1を触ってみようと思い、
Honoの練習やSlackのSlash Commandの練習を兼ねて遊んでみました。
Cloudflare D1とは
詳しくは上記を読んでいただくと良いと思いますが、
ざっくり言うとCDNのエッジ上で動作するsqliteです。(本日時点でまだOpen Alpha)
と、自分などが説明するよりも、日本語でD1についてざっくり理解するなら以下を読んでいただくのが手っ取り早いかと思います。
自分は普段はMySQLがメインですが、エッジ側で sqlite がどこでも使えるというのは個人的にもかなりインパクトがありました。
ちょっとした用途だったらDBも含めて全部サーバレスで済んでしまうというのは本当に素敵だしヤバいです(語彙)。
何を作ったのか?
先週、ちょうどタイミング良くというか、社内勉強会的のような場所においてファシリテーター(司会・進行・質問等を促す人)をサクッとランダムに指名出来るような事が出来たら良いなという話が出てきました。
こういう場合はちょっとしたGAS(Google Apps Script)などでちょろっとやったりするのがお手軽で良いのかもですが、
せっかくなのでHonoの練習やD1に触れてみたいということで、これをSlackのスラッシュコマンドから出来ないかということで試してみました。
/facilitator random
みたいにSlackのSlash Commandを打つと、参加者の中からランダムに指名してメンションを送るような感じです。
せっかくなのでデータもD1に突っ込んでしまって、
ユーザの登録/削除やSlackユーザーIDの設定なども全部Slackコマンドから行けると良いのではと考えました。
サンプルを見ながら実装してみる
以前発表したR2の実装は公式のexampleをカスタマイズしたものであり、
実はHonoを使うのも今回がほぼ初めてなので、
あたりを真似しつつ、公式のドキュメント
を写経しながらD1のCRUDを試していきました。
苦労した点
自分はTypeScriptどころかJavaScriptもあまり書いたことが無いので、ChatGPT先生に頻繁に聞きながら組み込んでいきました。
SlackコマンドはREST APIでは無かった
Honoを使うことのメリットとして、Routingを作ることが非常に簡単な事だと思うのですが、
当初、Slackコマンドにする前はシンプルなREST APIみたいなのをイメージしていたので、
app.get(
'/facilitator/list',
...
app.post(
'/facilitator/add',
...
app.post(
'/facilitator/update',
...
みたいに作っていたのですが、
SlackコマンドはRESTとは違ってエンドポイントに指定出来るURLが一つしかない事に気付きました…
仕方ないので、めちゃくちゃ可読性が悪くなってしまいますが、
力技のSwitch文で無理やり分岐させました。。
switch (command) {
case 'list':
console.log('GET list');
const { results } = await c.env.DB.prepare(
"SELECT username FROM users"
)
.all();
const usernames = results.map(function (obj) {
return obj.username;
});
return c.text(usernames.join('\n'));
break;
case 'dump':
Slackコマンドから渡されたペイロードの取り方が分からなかった
これは公式のヘルプ
をちゃんと読まなかったのが悪いのですが、スラッシュコマンドの後ろのパラメータの取り方が分からずハマりました。
console.dir(c.req)
や warn Dumper(c.req)
みたいな事が出来ないので(出来るんでしょうか?)
いろいろ試して結局 application/x-www-form-urlencoded
だったので、
を見て
const body = await c.req.parseBody();
const text = body.text;
const [command, ...args] = text.split(' ');
のようにして取れたのですが、果たしてこれでいいのかどうか、
普段のAPIなどではあまりペイロード自体を取得する事を意識してなかったのでハマりました。。
Only visible to you 問題
Slashコマンド自体を作るのも初めてだったので、
最初、
return c.text('<@${randomUser.slack_user_id}> test');
みたいにするだけで特定のChannelにメンションする事が出来るのかなと思っていたのですが、
「Only visible to you」ということでチャンネルの中で打っても自分自身しか見れないことが判明w
結局Slack APIを使うしか無いのか、、ということで探したところ
を見つけたのですが、 node_compat
の組み込み方が分からず時間も無かったので結局素のfetchでPOSTを投げることにw
const response = await fetch('https://slack.com/api/chat.postMessage', {
method: 'POST',
headers: {
'Authorization': `Bearer ${slackApiToken}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
'text': message,
'channel': channelName
})
});
とりあえず動いた
ハマりつつも、とりあえず最低限が動いたので以下にコミットしました。
こんな感じに動きます。
TODO
なお、あくまでも社内の遊び用なので現状では認証やバリデーションなどは組み込んでいません。
こちらについてはまた後日試してみようと考えています。
まとめ
- Hono はやっぱり使いやすかった
- しかし今回のSlackコマンドだと本来の力をうまく引き出せなかった
- Cloudflare D1 思ったよりも使えそう
- ちょっとしたAPIやサイトなら既に大体の事が出来るのでは?
- トランザクションは現状無いが、必要無いような用途であれば
- 早くGAして欲しい
- コストも本気で期待
- ちょっとしたAPIやサイトなら既に大体の事が出来るのでは?
- SlackのSlash Commandsは思ったよりも異世界だった
- 初物を欲張って複数同時にやるべきではなかった…
- 初めての言語等で分からない事があってもChatGPT先生に相談すればだいたいなんとかなりそう
D1をもっと実用的に
ORMだったり、D1をもっと実践的なアプリケーションのコードを書きたいのであれば、以下の記事がとても参考になりそうです。
本件を作り終わってから気づいたのですが、本格的なSlackアプリを作りたいなら以下のようなライブラリがあるようです。これを使えばだいぶ楽が出来ていたのかも…
その他参考