いいねカウントAPIをブックマークレットとChrome拡張機能から使う
LAP では、自分のサイトに <script> で埋め込む ブログパーツ として Like API を使った。本記事では同じ API を、自分のサイト以外でも使える ブックマークレット と Chrome 拡張機能 から呼び出す方法を扱う。
LAP の like.js には window.likeApi という共通インターフェースが公開されている。ブックマークレットはこれを読み込んで再利用する。Chrome 拡張機能は仕組み上 like.js を読み込めないため(理由は 4-4)、同じ API を content.js から直接呼ぶ。同じ API を別経路から使い回す体験がゴール。
LAP を完了している前提で進める。LAP で https://my-like-api.〇〇.workers.dev/like.js が公開されている状態を想定する。Cloudflare 側の追加作業は不要。
1. 何を作るか
Section titled “1. 何を作るか”LAP の Like API を、任意の Web ページで呼び出す2通りの仕組みを作る:
- ブックマークレット: ブラウザのブックマークから呼び出す(インストール不要、
javascript:URL を1個ブックマークに登録するだけ) - Chrome 拡張機能: 全ページにいいねボタンを自動で差し込む(拡張機能としてインストール)
全体像:
sequenceDiagram
participant in as ブックマークレット /<br>Chrome 拡張機能
participant pg as 訪問先の任意ページ<br>=フロントエンド
participant cf as Cloudflare(LAPで構築済み)<br>=バックエンド+データベース
in->>pg: <script> や <button> を差し込み
pg->>cf: like.js を読み込み(ブックマークレットのみ)
cf-->>pg: like.js(window.likeApi)
pg->>cf: fetch /api/like/:id(GET / POST)
Note over cf: Worker が D1<br>(pages / likes)を読み書き
cf-->>pg: { count, liked }
2つの方法の対比:
| ブックマークレット | Chrome 拡張機能 | |
|---|---|---|
| インストール | 不要(ブックマーク登録のみ) | 必要(拡張機能を読み込む) |
| 動作タイミング | ブックマーククリック時 | ページを開いた瞬間 |
| UI | アラート / コンソール | ページ上にボタンを差し込み |
| 配布 | URL(javascript: 文字列)を共有 | zip → Chrome ウェブストア |
| 必要な知識 | 1行のJS | manifest.json + content_scripts |
| 制約 | 訪問先サイトの CSP | Manifest V3 の permissions |
2. 事前準備
Section titled “2. 事前準備”2-1. GitHubでリポジトリを作成
Section titled “2-1. GitHubでリポジトリを作成”GitHub → New repository → リポジトリ名を my-like-extension で作成。Private にしておく。
名前は別のものでもよい。以降
my-like-extensionと出てきたら自分のリポジトリ名に読み替える。
このリポジトリには Chrome 拡張機能のソース(manifest.json 等)を入れる。ブックマークレットも一緒に管理する(記録用のテキストファイル)。Cloudflare へのデプロイは不要。
2-2. リポジトリを clone する
Section titled “2-2. リポジトリを clone する”cd ~/claudegit clone git@github.com:ユーザー名/my-like-extension.git2-3. Claude Code を起動
Section titled “2-3. Claude Code を起動”Claudeデスクトップアプリを起動。Code → New session → 作業フォルダ ~/claude/my-like-extension を指定。
3. 利用方法1: ブックマークレット
Section titled “3. 利用方法1: ブックマークレット”ブラウザのブックマークから JavaScript を実行する仕組み。javascript: で始まる URL をブックマークに登録しておくと、ブックマークをクリックしたときにその時開いているページ上でJSコードが実行される。
3-1. ブックマークレットの実装
Section titled “3-1. ブックマークレットの実装”ロジックは LAP の like.js に集約されているので、ブックマークレット側は like.js を動的に読み込んで、window.likeApi.like() を呼ぶだけにする。
Claude Code に依頼する。
以下の動作をするブックマークレット(javascript: で始まる1行のJS)を作ってください。
- <script> 要素を動的に作って document.body に追加し、 src="https://my-like-api.〇〇.workers.dev/like.js" を読み込む- 読み込み完了(onload)後に window.likeApi.like() を呼び出し、 返り値の { count, liked } をアラートで表示する 例: 「♥ 42 (新規)」「♥ 42 (既にいいね済み)」
IIFE(即時実行関数)でスコープを汚さないこと。生成後、bookmarklet.txt に保存してリポジトリで管理できるようにしてください。3-2. ブックマークに登録して使う
Section titled “3-2. ブックマークに登録して使う”- ブラウザのブックマークバー(または管理画面)から「新規ブックマーク」
- 名前 に任意(例:
Like!) - URL に上で生成された
javascript:...を貼り付け - 任意のページを開いて、ブックマークをクリック
- アラートに
♥ 42 (新規)のような表示
3-3. CSP(Content Security Policy)の制約
Section titled “3-3. CSP(Content Security Policy)の制約”ブックマークレットは外部スクリプト(like.js)を動的に読み込むため、訪問先サイトの CSP が厳しい場合は弾かれることがある。GitHub・Twitter / X・Slack・主要な銀行サイトなど、外部スクリプトを一切許可しないサイトでは動かない。
これはブックマークレットの仕様上の制約。動かない場合は、コンソールに Refused to load the script ... のような CSP 違反メッセージが出る。
CSP の緩いサイト(個人ブログ、note、Qiita、Zenn など)では問題なく動く。
CSP で弾かれる場合の代替手段が、次章の Chrome 拡張機能。拡張機能の
content.js自身のコードはページの CSP の制約を受けずに動けるため、ほぼ全サイトで動かせる(仕組みは 4-4)。
4. 利用方法2: Chrome 拡張機能
Section titled “4. 利用方法2: Chrome 拡張機能”任意のページに「♥ いいね」ボタンを自動で差し込む拡張機能。API は LAP のものをそのまま使うが、like.js は読み込まず、いいねのロジックは content.js に直接書く(理由は 4-4)。
4-1. Chrome 拡張機能の基本
Section titled “4-1. Chrome 拡張機能の基本”- 拡張機能 = フォルダに
manifest.json+ JS/HTML/CSS をまとめたもの - ブラウザに「開発者モード」で読み込ませて動かす
- 公開したければ Chrome ウェブストアに申請
主要な構成要素:
manifest.json: 拡張機能の設定ファイル(必須)content_scripts: 訪問先ページのコンテキストで動く JSbackground.js/service_worker: バックグラウンドで動くスクリプト(任意)popup.html: ツールバーアイコンクリック時のUI(任意)permissions/host_permissions: 必要な権限の宣言
LEX では content_scripts 中心で構成する。
4-2. manifest.json の作成
Section titled “4-2. manifest.json の作成”Chrome 拡張機能の manifest.json を作って。Manifest V3 で。
- name: Like Button Injector- version: 0.1.0- description: 任意のページに「いいね」ボタンを差し込む- content_scripts: matches に <all_urls>、js に content.js権限まわりの追加指定は不要。いいねの通信が通るのは host_permissions のような拡張機能の権限のおかげではなく、LAP の API 側が Access-Control-Allow-Origin: * を返しているため(→ 4-4)。
4-3. content.js にいいねロジックを実装する
Section titled “4-3. content.js にいいねロジックを実装する”ブックマークレットと違い、拡張機能では like.js を <script> で読み込む方式は使えない。content.js がページに差し込んだ <script> はそのページの CSP に従って弾かれるうえ、読み込めたとしても window.likeApi は content.js から見えない別ワールドに生まれる(理由は 4-4)。そこで、いいねのロジックは content.js に直接書く。呼び出す API は LAP と同じもの。
content.js を作って。以下の動作をする。
- ページ右下に固定表示のいいねボタンを差し込む (position:fixed; bottom:20px; right:20px; z-index は大きめ)- ロジックは ~/claude/my-like-api/public/like.js(LAPで作ったもの)に揃える: pageId の正規化と localStorage の UUID(like-uuid)の扱いを同じにして、 https://my-like-api.〇〇.workers.dev/api/like/<pageId> に GET でカウントを取得、ボタンクリックで POST、 返ってきた count でボタンの「♥ 数字」表示を更新する- 外部スクリプトは読み込まない(ロジックはすべて content.js の中に書く)pageId と UUID の決め方を
like.jsと揃えておくと、ブログパーツ・ブックマークレット・拡張機能のどれから押しても、同じページのカウントが共有される。like.jsが手元にない場合は、その中身をコピーしてプロンプトに貼るのが確実。
ブックマークレットは
like.jsを読み込んでwindow.likeApiを使い、拡張機能は同じ API をcontent.jsから直接呼ぶ。「フロントの届け方が違うだけで、バックエンドは1つ」。
4-4. 分離ワールドと CSP・CORS の関係
Section titled “4-4. 分離ワールドと CSP・CORS の関係”content.js は訪問先ページの DOM を触れるが、JavaScript としては「分離されたワールド」(isolated world)で動く。この仕組みを押さえると、方式の違いがそのまま説明できる。
- ページの CSP の影響を受けるかどうかは「コードがどこで動くか」で決まる。
content.js自身のコードはページの CSP の制約を受けない。ブックマークレットが弾かれる CSP 厳しめのサイトでも拡張機能が動くのは、これが理由 - ただし
content.jsがページに差し込んだ<script src="...">は、ふつうのページ内スクリプトとしてそのページの CSP に従う。like.js読み込み方式が使えないのはこのため(ブックマークレットと同じ制約に戻ってしまう) - 変数も共有されない。仮に
like.jsを読み込めても、window.likeApiはページ側のワールドに作られ、content.jsからは見えない - fetch は通常の CORS のルールに従う。LAP の API は
Access-Control-Allow-Origin: *を返すので、どのサイトの上からでも呼べる。host_permissionsを宣言しなくても通るのはこのため
まとめると「CSP はコードの置き場所の問題、CORS は通信相手の問題」。前者は拡張機能の分離ワールドが解決し、後者は LAP の API 設計(
*許可)が解決している。
4-5. ローカルで読み込んで動作確認
Section titled “4-5. ローカルで読み込んで動作確認”- Chrome のアドレスバーに
chrome://extensions/を入力 - 右上の デベロッパーモード をオン
- パッケージ化されていない拡張機能を読み込む をクリック
~/claude/my-like-extensionフォルダを選択- 任意のページを開く → 右下に「♥」ボタンが出る → クリックでカウント増加
問題があれば chrome://extensions/ で拡張機能の エラー や Service worker のログ を確認する。
4-6. (任意) Chrome ウェブストアへの公開
Section titled “4-6. (任意) Chrome ウェブストアへの公開”個人で使うだけなら不要。公開したい場合:
- Chrome ウェブストアデベロッパー登録(初回 $5)
- アイコン・スクリーンショット・説明文を用意
- zip にして審査に出す
- レビュー通過後に公開
審査では「権限の妥当性」「プライバシーポリシー」「説明文の正確さ」などが見られる。
5. 2つの方法の使い分け
Section titled “5. 2つの方法の使い分け”| 状況 | 向いている方法 |
|---|---|
| 自分専用、すぐ試したい | ブックマークレット |
| 友達にも配りたい | ブックマークレット(URL共有) |
| 全ページに自動で出したい | Chrome 拡張機能 |
| CSP 厳しめサイトで動かしたい | Chrome 拡張機能 |
| Chrome ウェブストアで配布したい | Chrome 拡張機能 |
両方とも本質的には「LAP の API を呼ぶだけ」。実装の手間と機能性のトレードオフで選ぶ。
6. 制約と注意点
Section titled “6. 制約と注意点”- CSP: 訪問先サイトの CSP がブックマークレットを弾く場合あり(拡張機能は
content.jsにロジックを持つため影響を受けない) - DOM 構造: サイトによっては差し込むボタンの位置調整が必要
- SPA: ページ遷移が JS で行われるサイトでは、URL が変わってもボタンが更新されない場合がある(
MutationObserverなどで対応) - プライバシー:
<all_urls>権限は強い。本番運用ならmatchesを絞る - LAP 側の停止: LAP の API が落ちると、ブックマークレットも拡張機能も動かない(共通の依存)
7. 発展課題
Section titled “7. 発展課題”- popup UI: ツールバーアイコンから現在ページのカウントを表示
- オプションページ: 動作対象サイトを制限できる設定画面
- chrome.storage: 拡張機能スコープで UUID を保持(localStorage より安定)
- ボタンの見た目: ハートアニメーション、テーマ対応
- 複数サービス対応: LAP 以外の API も叩けるようにする(設定で API URL を切り替え)
- ブックマークレット集: 同じパターンで「閲覧履歴を記録」「ページ要約を送る」など、他用途のブックマークレットも作る