Cloudflare Worker を使うと静的サイトにサーバーレスな受付口を作れて便利

Hugo のような静的サイトは、HTML を配信するだけならとても軽いのですが、 POST /api/... のような 小さい受付口 を足したくなることがあります。 例えば次のような用途です。 記事末尾の軽いリアクション お問い合わせの簡易受付 Slack への通知トリガー ちょっとした webhook の受け口 このとき、サイト全体を動的構成へ寄せるほどではないが、POST /api/... だけは欲しい、という場面があります。 そういう用途にちょうどよいのが Cloudflare Worker です。 今回は、Hugo サイトに軽いリアクション導線を追加した実例をもとに、 Cloudflare Worker + D1 + Slack でサーバーレスな受付口を作る方法 を、実際のファイルと設定をそのまま出しながらまとめます。 Cloudflare Worker と D1 は何をするものか まず役割を分けておきます。 Cloudflare Worker Cloudflare Worker は、Cloudflare のエッジ上で動くサーバーレス実行環境です。 ざっくり言うと、Cloudflare 側に小さな API を置ける仕組み です。 今回の用途では、POST /api/like を Worker で受けます。 Cloudflare D1 Cloudflare D1 は、Cloudflare が提供している SQL データベースです。 SQLite に近い感覚で使え、Worker から直接読み書きできます。 今回の用途では、次のようなイベント記録を保存します。 どの記事に対するリアクションか 同じ visitor がすでに送っていないか いつ送られたか Slack Incoming Webhook 通知先です。 新しいイベントがあったときだけ Slack に飛ばします。 ...

公開: 2026年4月22日 · Toshihiko Arai

Amazonの領収書のPDF保存をラクにする自作JS

個人事業主・フリーランスの方は、確定申告の時期になりましたね。私はAmazonで購入することが多く、領収書を電子保存しておく必要があります。ところが、これまで使っていたChrome拡張(Amazon領収書一括印刷系)が動かなくなってしまいました。 そこで仕方なく、Amazonの注文履歴から「領収書等」→「印刷可能な注文概要」を開き、黄色の「印刷」ボタンを押してPDF保存……という作業をひたすら繰り返していました。 さらに困るのが、PDF保存時のファイル名がデフォルトで「注文の詳細.pdf」などになってしまう点です。注文番号(例:XXX-XXXXXXX-XXXXXX)をファイル名にしたいので、毎回注文番号をコピーしてファイル名に貼り付けてから保存していました。これが地味に面倒でした。 「注文番号をDOMから拾って、印刷まで自動化できそうだな」と思い、ChatGPTに相談して作ったのが以下のブックマークレットです。 (ブックマークのURL欄に javascript: から始まるコードを貼り付けて実行する、あのやつです。) javascript:(()=>{const old=document.title;const re=/\b(?:\d{3}|[A-Z]\d{2})-\d{7}-\d{7}\b/;const m=document.body&&document.body.innerText&&document.body.innerText.match(re);const id=(m&&m[0])||prompt("注文番号(例: 250-3207065-8648623 / D01-5274236-7074631)");if(!id)return;document.title=id;window.addEventListener("afterprint",()=>{document.title=old},{once:true});window.print();})(); Note ブックマークは「ページを保存する場所」ではなく、実は“自分だけのショートカットキー”にもできます。 URL欄に javascript: を入れるだけで、ページ上の定型作業をワンクリックで実行可能。 拡張機能みたいにストア審査もアップデート追従も不要で、壊れにくいのが強みです。 必要なページでだけ動くので、普段のブラウジングを汚さないのも地味に便利。 小さな自動化を積み上げると、ブラウザが“作業ツール”に変わります。 使い方 Chromeのブックマークに「新しいブックマーク」を追加します ※図① 名前は適当に付けて、URL欄に上のコードをそのまま貼り付けます Amazonの領収書(印刷可能な注文概要)ページを開いた状態で、そのブックマークを実行します 印刷画面が開くので「PDFとして保存」を選ぶと、ファイル名に注文番号が入った状態になっているはずです ※図② これなら、手作業で1年分の領収書を保存する場合でも、そこまで苦にならないと思います。 参考になればうれしいです。 追記:注文日も拾って YYYYMMDD_注文番号 で保存する PDFのファイル名を注文番号だけでなく、注文日も付けて 20251231_503-1737382-0797449 のようにすると、Finderでソートしやすく後から探すのがさらにラクになります。 印刷ページ内の「注文日 2025年12月31日」をパースして、ファイル名の先頭に付ける版はこちらです。 javascript:(()=>{const old=document.title;const orderRe=/\b(?:\d{3}|[A-Z]\d{2})-\d{7}-\d{7}\b/;const dateNearRe=/注文日[\s\S]{0,80}?(\d{4})年(\d{1,2})月(\d{1,2})日/;const dateRe=/(\d{4})年(\d{1,2})月(\d{1,2})日/;const t=document.body&&document.body.innerText||"";const om=t.match(orderRe);const dm=t.match(dateNearRe)||t.match(dateRe);const orderId=om?.[0]||prompt("注文番号(例: 250-3207065-8648623 / D01-5274236-7074631)");if(!orderId)return;const y=dm?.[1],m=dm?.[2],d=dm?.[3];const pad=n=>String(n).padStart(2,"0");const prefix=(y&&m&&d)?`${y}${pad(m)}${pad(d)}_`:"";document.title=`${prefix}${orderId}`;window.addEventListener("afterprint",()=>{document.title=old},{once:true});window.print();})(); これで、保存ダイアログを開いた時点でファイル名が YYYYMMDD_注文番号 になっているはずです。注文日が見つからないページでも、注文番号だけで動くようにしてあります。 ちなみに上のコードを整形すると次のようになります。 javascript:(() => { const old = document.title; const orderRe = /\b(?:\d{3}|[A-Z]\d{2})-\d{7}-\d{7}\b/; const dateNearRe = /注文日[\s\S]{0,80}?(\d{4})年(\d{1,2})月(\d{1,2})日/; const dateRe = /(\d{4})年(\d{1,2})月(\d{1,2})日/; const t = document.body && document.body.innerText || ""; const om = t.match(orderRe); const dm = t.match(dateNearRe) || t.match(dateRe); const orderId = om?.[0] || prompt("注文番号(例: 250-3207065-8648623 / D01-5274236-7074631)"); if (!orderId) return; const y = dm?.[1], m = dm?.[2], d = dm?.[3]; const pad = n => String(n).padStart(2, "0"); const prefix = (y && m && d) ? `${y}${pad(m)}${pad(d)}_` : ""; document.title = `${prefix}${orderId}`; window.addEventListener("afterprint", () => { document.title = old }, {once: true}); window.print(); })(); 注意点 Amazon側のページ構成やリンク文言(「領収書」「領収書等」など)は変更されることがあります。ブックマークレットが動かなくなったら、まず実際のページ表記を確認してください。 一度に大量の注文を処理するとタブやダイアログが増えるので、数件ずつ実行するのが安全です。 関連記事 C++でOpenCVをはじめる|撮影したレシートを自動でトリミング … 紙のレシートを撮影して自動でトリミング・補正する画像処理の例 バニラJSのモダンな書き方・チートシート … ブックマークレットを書くときのJavaScript逆引きに Cloudflare Workerでサーバーレスな受付口を作る … 静的サイトに小さな処理を足したいとき 関連商品をAmazonで探す 確定申告ソフトをAmazonで探す ドキュメントスキャナーをAmazonで探す JavaScript入門書をAmazonで探す

公開: 2026年1月6日 · 更新: 2026年5月30日 · Toshihiko Arai

Webプッシュについて調査忘備録

Web Push API を使うと、ブラウザを開いていないユーザーにも通知を届けられます。仕事で実装する機会があり、調べた内容を忘備録としてまとめておきます。Service Worker・VAPID・Push サービスの 3 つが連携する仕組みを理解すれば、あとはライブラリが面倒な暗号化処理を代わりにやってくれます。サーバーサイドの実装は PHP(minishlink/web-push)で試す手順を紹介します。 Webプッシュのざっくり概要 Web プッシュは、ブラウザと OS の通知機能を使って「ブラウザを開いていなくても」通知を届ける仕組みです。 ユーザーがブラウザで「通知を許可」する ブラウザが Push サービス(FCM / APNs など)とやり取りして 購読情報(Subscription) を発行 サーバー側は、その購読情報(endpoint / p256dh / auth)を DB に保存 通知を送りたいときは、サーバーアプリケーションで Push サービスへリクエストを投げる Push サービス → OS の通知チャネル → Service Worker → 通知表示 という流れになります。 Service Worker が Web Push API の橋渡しを担います。Service Worker は JS で実装されたプログラムで、ブラウザにインストールされ、メインスレッドとは別スレッドで動作します。また、Service Worker を登録するページおよび Service Worker スクリプトは HTTPS で配信されている必要がある点に注意してください(ただし localhost は例外的に http でも動作します)。 もう少し詳しく もう少し詳しく説明すると、Web プッシュでは購読された時点でブラウザ側で一意のエンドポイントが発行されます。 このエンドポイントはブラウザベンダーごとに異なり、Google Chrome なら FCM(https://fcm.googleapis.com/...)、Safari / iOS なら APNs 経由のエンドポイントという形になります。 ...

公開: 2025年11月30日 · 更新: 2026年5月23日 · Toshihiko Arai

バニラJSのモダンな書き方・チートシート

React や Vue などのフレームワークを使わず、ブラウザ標準のJavaScriptだけで書くとき(いわゆるバニラJS)に毎回調べがちな構文をまとめました。非同期処理・配列操作・オブジェクトコピー・DOM操作・ストレージなど、実務でよく使うパターンを用途別に整理しています。Node.js でも動く構文は多いですが、主にブラウザ環境を想定しています。 ストレージ — sessionStorage / localStorage sessionStorage.setItem('myData', '保存したい内容'); // 保存 const savedData = sessionStorage.getItem('myData'); // 読み出す sessionStorage.removeItem('myData'); // 削除 sessionStorage.clear(); // 全て削除 sessionStorage は同一タブ内でのみ保持され、タブを閉じると破棄されます。最大容量はブラウザによりますがおおむね5MB。保存形式は文字列のみなので、オブジェクトを保存したい場合は JSON.stringify() で変換が必要です。 sessionStorage を localStorage に書き換えるとデータを永続化できます。 オブジェクトの凍結 — Object.freeze const Trial = { ok: true, ng: false }; Object.freeze(Trial); // オブジェクトを凍結 Trial.ok = false; // 非 strict mode ではエラーにならないが値は変わらない(strict mode では TypeError) console.log(Trial.ok); // true const ではプロパティの書き換えを防げません。Object.freeze() はトップレベルのプロパティ変更を防ぎますが、浅い凍結なので入れ子のオブジェクトは変更できます。JS で Enum を表現したいときに便利です。 非同期処理 — fetch / async / await async function fetchUser(userId) { const response = await fetch(`/api/user/${userId}`); if (!response.ok) throw new Error("通信エラー"); const data = await response.json(); return data; } fetchUser(1).then(user => { console.log(user.name); }).catch(err => { console.error(err); }); fetch() はブラウザ組み込みのHTTP通信APIです。await で非同期処理の完了を待てるため、Promise のチェーンよりも読みやすくなります。await を使う関数は async で宣言する必要があります。 ...

公開: 2025年9月3日 · 更新: 2026年5月24日 · Toshihiko Arai