コンテンツにスキップ

Claude Code と Claude API で AI を組み込んだWebアプリを作って公開する

Claude を自分のアプリの中から呼ぶ。すると、これまで自分が Claude に頼んでいたことを、アプリの利用者が頼めるようになる

ただしこの記事は、これまでのハンズオンとひとつだけ性格が違う。必ずお金がかかる。 無料枠は無く、クレジットを買わないと1回も動かない。だから 任意の記事 として置いてある。

そのぶん、この記事の重心は作り方ではなくお金のほうにある。上限を先に決める。どのモデルを使うかを、カタログではなく実際に試して決める。1回いくらかかっているのかを自分で測れるようにする。作例そのものは、入力欄がひとつだけの小さなものにする。

前提は Wranglerハンズオン を終えていること。Cloudflare にデプロイできるところまで来ていればよい。

似た記事に Workers AI で5つのAIデモを作る がある。あちらは Cloudflare の AI を使うので キーが要らず、無料枠がある。何度試しても費用がかからないので、5つのデモを並べて一通り試せる形にしてある。

この記事はその逆になる。

Workers AI(WAI)Claude API(この記事)
APIキー要らない要る
無料枠1日1万 Neurons無い
日本語の質用途による高い
作例の数5つ1つだけ

呼ぶたびにお金が減るので、作例は1つに絞る。 そのかわり、キーの扱いと、使う金額の決め方を最後まで丁寧にやる。

なお 公開するWebアプリの地雷を避ける のスキルを入れてあると、この記事で出てくる注意のいくつかは Claude 側が自動的に避けてくれる。入れていなくても進められる。

タイトル案メーカー。記事の要旨を貼ると、タイトル案を5つ返す。それぞれに「誰を惹きつける狙いか」が1行つく。

画面にあるのは入力欄がひとつだけ。1回のやりとりで完結し、会話の履歴は持たない。

なぜこの形にするか。会話履歴を持たないと、サーバー側に覚えておくものが何もない。 データベースもセッション管理も要らず、この記事の本題である課金とセキュリティに集中できる。

全体像はこうなる。

sequenceDiagram
    participant pg as ブラウザ<br>=フロントエンド
    participant wk as Pages Functions<br>=バックエンド
    participant cl as Claude API
    pg->>wk: 画面(index.html)を要求
    Note over wk: 合言葉を確かめる。<br>通らなければ401
    wk-->>pg: 画面を配信
    pg->>wk: fetch POST /api/generate<br>記事の要旨を送る
    wk->>cl: APIキーを付けて呼ぶ
    cl-->>wk: タイトル案
    wk-->>pg: JSON で返す

APIキーが出てくるのはサーバー側だけ。 ブラウザには一度も渡らない。

2-1. ベース名を決めて作業フォルダを用意する

Section titled “2-1. ベース名を決めて作業フォルダを用意する”

これまでのハンズオンと同じように、自分だけの ベース名 を1つ決める。

  • 例: titler-yto(イニシャル)、titler-0822(日付)など、他と被らなそうなもの
  • 短く、英小文字と数字とハイフンだけにしておく

ベース名はそのまま 公開URLhttps://<ベース名>.pages.dev)になる。この記事では my-titler と表記して進める。

Finder で ~/claude フォルダ(なければ作る)の中に、ベース名の作業フォルダを作る(例: my-titler)。

Claudeデスクトップアプリを起動し、Code(Claude Code)で作業フォルダを開く(例: ~/claude/my-titler)。

モード選択は 編集を受け入れる にしておく(自動 にしない)。この記事ではお金が動くコマンドを実行するので、一つずつ内容を見て承認しながら進める。

セッションを開いたら、まずベース名を覚えさせる。先頭の my-titler を自分のベース名に置き換えて渡す。

Claude
このプロジェクトのベース名は my-titler です。これを CLAUDE.md に記録してください。
- アプリ名・プロジェクト名 = ベース名
これ以降、私のプロンプトの <ベース名> はベース名に読み替えて作業してください。

2-3. 作るものをエージェントに覚えさせる

Section titled “2-3. 作るものをエージェントに覚えさせる”

アプリの仕様を、先に CLAUDE.md に書いてしまう。 以降は「仕様のとおりに」と参照するだけでよくなる。

Claude
これから作るアプリの仕様を CLAUDE.md に書いてください。
## 作るもの:タイトル案メーカー
- 入力欄は1つだけ。記事の要旨を貼る
- タイトル案を5つ返す。それぞれに「誰を惹きつける狙いか」を1行添える
- 1回きりのやりとり。会話履歴は持たない
- サイト全体に合言葉をかけ、通らないと何も見られないようにする
## 作り方の決まりごと
- Claude API を呼ぶのはサーバ側だけ。ブラウザから直接呼ばない
- APIキーは Secret に置く。コードに書かない
- モデルはサーバ側で固定する。利用者の入力からは受け取らない
- max_tokens と入力の長さに上限を設ける

なぜ先に書くのか。仕様を毎回のプロンプトに書くと、作りたいものの話と、どう作るかの話が混ざる。先に1回書いておけば、以降のプロンプトは「どう作るか」だけになる。この考え方は D1ハンズオン と同じ。

ただし、アプリが Claude に送る指示(システムプロンプト)はここに書かない。 CLAUDE.md は Claude Code への指示を置く場所で、そこにアプリの指示を混ぜると、どちらへの指示なのか読み分けられなくなる。システムプロンプトはコードの中に置く(6章)。

API の課金は前払いのクレジット方式になる。先にお金を入れておき、使うと減っていく。残高がゼロになれば止まる。

普段 Claude Code で使っているのは、多くの場合サブスク側になる。同じ Claude でも、アプリの中から呼ぶときは API 側の財布から出る、と分けて考える。

作り始める前に、財布のほうを整える。

順番はこうなる。

  1. ハンズオン用のワークスペースを作る
  2. クレジットを買う
  3. そのワークスペースに月間の上限とメール通知を設定する
  4. そのワークスペースの APIキーを作る
  5. キーをファイルに置き、その場で Git の管理外にする

Claude Console を開く。ここは Claude と会話する画面(claude.ai)とは別のサイト

設定ワークスペースワークスペースを作成 と進む。名前は my-titler のように、このアプリ専用と分かるものにする。

なぜ専用のワークスペースを作るのか既定の「Default」ワークスペースには上限を設定できない。専用のものを作れば、そこに上限とキーを閉じ込められる。事故が起きても、その中で止まる。

作成時に「ワークスペースの地域」を選ぶ欄がある。現時点では US だけで、作成後は変更できない。そのまま進めてよい。

ワークスペースを作成するダイアログ。名前・カラー・ワークスペースの地域を指定する

名前とカラーを決めるだけ。地域は US のみで、作成後は変更できない

設定請求クレジットを購入

金額は $5 から 選べる。この記事の作例なら $5 で足りる(何回呼べるかは5章で計算する)。

購入時に注意する点がふたつある。

  • 自動リロードはオフのままにする。 残高がゼロになったら止まる、という状態にしておく。オンにすると、減るたびに自動で買い足される。止まるべきときに止まらなくなる
  • クレジットは購入から1年で失効する。 返金もされない。使う分だけ買う

クレジット購入画面。$5・$20・$100・その他から金額を選ぶ。右側に購入概要が出る

$5 から選べる。右側の注記に「購入から1年で失効します」と書かれている

日本から買うと消費税が乗る。$20 を買った実例では、請求は $22.00 だった。

4-3. 上限を設定する(買った直後にやる)

Section titled “4-3. 上限を設定する(買った直後にやる)”

クレジットを買うと、月間の利用上限がいきなり $200,000 になる。 少額を買っただけでもそうなる。

購入直後の請求ページ。クレジット残高 $20.00 の下に、月間支出上限 $200,000 と表示されている

$20 だけ買った直後の画面。月間支出上限が $200,000 になっている。赤い注記は「自動リロードはオフです」

だから買った直後に、自分で上限を絞る。

作ったワークスペースを選び、管理利用上限 を開く。

  • 月間上限制限を変更 から金額を入れる。$5 など、自分が失っても平気な額にする。整数のドル単位でしか設定できない
  • メール通知通知を追加 から金額を入れる。上限の手前、たとえば $1 にしておくと、そこに達した時点で気づける

ワークスペースの利用上限ページ。月間上限が $0.00/$5.00 と表示され、隣にメール通知の設定がある

上限を $5 に絞ったあと。左が使用額と上限、右がメール通知

合言葉が漏れても、コードにバグがあっても、他人に見つかって使われても、失う額はここで決めた金額で止まる

同じワークスペースの APIキーキーを作成

最初に「ID連携を使用すれば、APIキーは不要です」という案内が出る。クラウドや CI 向けの仕組みで、個人開発では使わない。APIキーで続ける を選ぶ。

「ID連携を使用すれば、APIキーは不要です」という案内。下に「ID連携を設定する」と「APIキーで続ける」のボタンがある

キーを作ろうとすると、まず「キーは要らないかも」と勧められる。ここでは下の APIキーで続ける を選ぶ

作成の画面では3つを指定する。

項目何を入れるか
ワークスペースさきほど作ったものになっていることを確認する
名前my-titler-dev など、どこで使うキーか分かるもの
有効期限7日間30日間 を選ぶ

有効期限は 3時間/1日/7日間/30日間/カスタム/なし から選べる。「なし」だけ赤字で表示される。期限を付けておけば、消し忘れても自然に切れる。

APIキーの有効期限を選ぶドロップダウン。3時間・1日・7日間・30日間・カスタム・なしが並び、「なし」だけ赤字

「なし」だけ赤い。無期限は避けたほうがよい、と画面が言っている

作成すると、キーが一度だけ表示される。この画面を閉じると二度と見られない。

4-5. キーを置く場所を決める(先に決めておく)

Section titled “4-5. キーを置く場所を決める(先に決めておく)”

作業フォルダに .dev.vars というファイルを作り、そこに書く。このコマンドは自分でターミナルに入力する(キーを Claude に渡さないため)。

Terminal window
cd ~/claude/my-titler
echo 'ANTHROPIC_API_KEY=ここにキーを貼る' > .dev.vars
echo 'SITE_PASSWORD=自分で決めた合言葉' >> .dev.vars

続けて、Git の管理外にする。ここは頼んでよい。

Claude
.gitignore を作って、.dev.vars と .wrangler/ と node_modules/ を
Git の管理から外してください。そのあと、.dev.vars が本当に外れているか
git check-ignore で確かめて結果を見せてください。

確かめるところまで頼むのが大事になる。作っただけでは、実際に除外できているか分からない。

.dev.vars は Wrangler がローカル実行のときに読むファイル。本番の Cloudflare には別の方法で渡す(8章)。同じ値を2か所に置くことになるが、どちらもコードには書かない。

5. いくらかかるか、どのモデルを選ぶか

Section titled “5. いくらかかるか、どのモデルを選ぶか”

料金は、使ったトークンの量で決まる。トークンは文章を細かく切った単位で、おおよそ日本語1文字が1トークン前後になる。

主なモデルの単価は次のとおり(100万トークンあたり)。

モデル入力出力性格
Claude Opus 5$5$25最も賢い。複雑な仕事向け
Claude Sonnet 5$2$10日常の仕事に十分
Claude Haiku 4.5$1$5最も速く安い

出力の単価は入力の5倍になっている。長く喋らせるほど高くつく、と覚えておく。

5-1. 呼ぶ前に、どこまで分かるか

Section titled “5-1. 呼ぶ前に、どこまで分かるか”

入力の長さは、実際に呼ぶ前に無料で数えられる。 トークンを数えるための専用の窓口があり、これは課金されない。

ただし注意がある。クレジットの残高がゼロだと、この無料の窓口も使えない。無料なのは料金の話で、残高が要らないという意味ではない。

一方で、出力の長さは呼んでみるまで分からない。何文字返すかは Claude が決めるため。だから最終的には1回呼んで測ることになる。max_tokens で上限を決めておけば、1回あたりの最悪額は先に確定できる

どのモデルを使うかは、カタログを読むのではなく、自分の用途で試して決める

まだアプリは作っていないが、それで構わない。 画面が無くても、Claude Code が .dev.vars のキーを使って直接 API を呼び出せる。先に決めておけば、実装のときにはもうモデルが決まっている。

試す文章を1つ用意して、プロンプトの中に貼る。自分がこれから書く記事の要旨でよい。

Claude
タイトル案メーカーで使うモデルを決めたいので、比べてください。
CLAUDE.md の仕様に沿ったシステムプロンプトを作り、下の「記事の要旨」を
Haiku 4.5 と Sonnet 5 の両方に投げて、次を並べてください。
- 実際に返ってきたタイトル案(そのまま見せる)
- 入力と出力のトークン数
- 1回あたりのコスト(円でも)
- 上限 $5 なら何回呼べるか
【記事の要旨】
(ここに、自分が試したい文章を貼る)
APIキーは .dev.vars にあります。結果は notes/ にメモとして残してください。

両方に同じ文章を投げるのが大事になる。片方だけ長い文章を渡すと、質の差なのか入力の差なのか分からなくなる。プロンプトに1つだけ貼れば、Claude が同じものを両方に使う。

結果はメモに残させる。 あとで「なぜこのモデルにしたのか」を思い出せる。作業メモをリポジトリに残す考え方は 作業メモをリポジトリに残す にある。

モデル名は呼び名で伝えればよい。 コードの中では claude-sonnet-5 のような正確な文字列が要るが、こちらが覚えておく必要はない。「Sonnet 5」「Haiku 4.5」と書けば、Claude が正しいものに読み替えてくれる。

実際にやってみた。投げたのはこの2つになる。まず、CLAUDE.md の仕様から起こした指示。これがのちに、アプリがサーバー側に持つ指示(システムプロンプト)になる。

あなたは記事のタイトルを考える編集者です。
渡された記事の要旨を読み、タイトル案を5つ出してください。
それぞれのタイトルの下に、そのタイトルが誰を惹きつける狙いなのかを1行で添えてください。
前置きや後書きは書かず、5つの案だけを返してください。

そして、両方に投げた記事の要旨。こちらは自分で用意する(さきほどのプロンプトに貼ったもの)。

この記事では、Cloudflare D1 を使って掲示板アプリを作り、Pages Functions から
読み書きする方法を解説する。データベースを触ったことがない人でも、
Claude Code に頼めば SQL を書かずに進められる。

Claude Sonnet 5 が返してきたもの(先頭の2案)。

1. 「Cloudflare D1×Pages Functionsで作る掲示板アプリ入門」
狙い:Cloudflareの技術構成を具体的に知りたいエンジニアを惹きつける
2. 「SQLを書かずにデータベースアプリを作る:Claude Code×D1実践ガイド」
狙い:データベース未経験でAI活用に興味がある初心者層を惹きつける

Claude Haiku 4.5 が返してきたもの(同じく先頭部分)。

Cloudflare D1 とページ機能で初心者向け掲示板アプリを構築する
初心者でも SQL 不要で作れる実践的なチュートリアルを求める開発者
Claude Code を使って SQL を書かずに Cloudflare 掲示板を構築する

数字のほうも並べる。

Claude Haiku 4.5Claude Sonnet 5
入力トークン数193204
出力トークン数280480
1回あたり約0.24円約0.78円
上限 $5 なら約3,100回約960回

ここから3つ分かる。

指示どおりの形で返すかどうかに差が出た。 Sonnet 5 は番号と「狙い」のラベルを付け、そのまま画面に出せる形で返した。Haiku 4.5 はタイトルと狙いが交互に並ぶだけで、どこまでが1案なのか読み取りにくい。画面にそのまま出す前提だと、この差は表示崩れになる。

公平に見ると、このシステムプロンプトは番号を付けろとは書いていない。「それぞれのタイトルの下に1行添えて」としか言っていないので、Haiku 4.5 が番号を振らなかったこと自体は指示違反ではない。同じ指示から、読める形に整えてくるかどうかの差になる。指示を細かく書き込めば Haiku 4.5 でも整うかもしれないが、そこまで書き込まないと崩れるという性質自体が選ぶときの材料になる。

同じ日本語でも、モデルによってトークン数が違う。 193 と 204 で、約6%ちがう。つまり 単価と文字数の掛け算では計算できない。モデルごとに数える必要がある。

コストの差は、単価の差より大きくなる。 単価は2倍差なのに、1回あたりでは3倍以上ひらいた。Sonnet 5 のほうが長く返すため。安いモデルを選べば安く上がる、とは限らない。

数字と出力が並んだら、自分で選ぶ。Claude に「どちらがいいですか」と決めさせない。実測を並べた意味がなくなる。

聞くとしたら、判断そのものではなく違いのほうを聞く。

Claude
2つの出力の違いを、形式・語彙・長さの観点で説明してください。
どちらを選ぶべきかは書かなくていいです。

ここでは Sonnet 5 を選んだことにして進める。理由は、返ってきた形をそのまま画面に出せることと、どちらにしても1回1円未満なのでここで削る意味が薄いこと。

決めたら、それも覚えさせる。

Claude
使うモデルは Sonnet 5 に決めました。理由もあわせて CLAUDE.md に
追記してください。経緯を書いたメモへのリンクも入れてください。

決めたことは CLAUDE.md、決めるまでの経緯は notes/ この住み分けにしておくと、あとから読む自分が迷わない。

仕様は既に CLAUDE.md にある。だからここでは「どう作るか」だけを頼む。

Claude
CLAUDE.md の仕様のとおりに、タイトル案メーカーを作ってください。
構成は Cloudflare Pages + Pages Functions で、ビルドの手順が要らない形に
してください。npm のパッケージは入れず、素の fetch で Claude API を呼びます。
- public/index.html に画面。入力欄1つと、結果の表示
- functions/api/generate.js で Claude を呼ぶ
- アプリが Claude に送るシステムプロンプトは、その中に置く

なぜ公式の SDK を使わないのか。素の fetch なら追加のパッケージが要らず、ビルドの手順も増えない。サーバー側から呼ぶだけならこれで足りる。パッケージが増えないぶん、後から読み返すのも楽になる。

できあがった functions/api/generate.js のうち、Claude を呼んでいるのはこの部分だけになる。

const res = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: {
"x-api-key": env.ANTHROPIC_API_KEY,
"anthropic-version": "2023-06-01",
"content-type": "application/json",
},
body: JSON.stringify({
model: MODEL,
max_tokens: MAX_TOKENS,
system: SYSTEM_PROMPT,
messages: [{ role: "user", content: summary }],
}),
});

読むべきところは4つ。

項目意味
x-api-keyここでキーを使う。この処理はサーバー側で動くので、ブラウザには渡らない
model使うモデル。コードの中で固定する
max_tokens返させる長さの上限。ここが1回あたりの最悪額を決める
systemアプリが Claude に渡す役割の指示

6-1. なぜブラウザから直接呼ばないのか

Section titled “6-1. なぜブラウザから直接呼ばないのか”

いま作ったものは、Claude を呼ぶ処理をサーバー側に置いている。画面から /api/generate に送り、そこから Claude を呼ぶ。画面が直接 Claude を呼びに行っているわけではない。

実はこの一手間は、省くこともできる。画面に埋め込んだ JavaScript から、Claude API を直接呼び出すという形になる。専用のヘッダをひとつ付けると、その経路が開く。サーバー側の処理は要らなくなり、作りは確かに簡単になる。

しかし、問題がある。 画面の JavaScript は、見に来た人のパソコンの上で動いている。自分のサーバーではない。中身はブラウザの開発者ツールで誰でも読めるし、そこから送ったリクエストも、ヘッダごと同じ画面に表示される。

つまりそこに APIキーを書けば、見に来た人全員に配っているのと変わらない。拾った人はそのキーで好きなだけ Claude を呼べて、請求はこちらに来る。しかも自分のアプリからではなく、その人の手元から呼ばれるので、こちらの画面には何も残らない。

そのヘッダの名前には dangerous(危険)という語が入っている。名前そのものが警告になっている。 身内だけで使う道具や、利用者が自分のキーを入力して使う形なら出番はあるが、不特定多数に公開するものでは使わない

だからキーはサーバー側に置く。画面には渡さない。

AI を呼ぶアプリを、鍵をかけずに公開URLに置かない。 画面が見えるということは、その画面から AI を呼べるということで、呼ばれた分の料金はこちらに来る

かける相手は、特定のページではなく公開URLの全体にする。https://<ベース名>.pages.dev/ を開いた時点で合言葉を求め、その配下のURLにも一切アクセスさせない形にする。

  • 作りかけを置いておける。 公開URLに上げないと確認できないが、まだ人に見せる段階ではない
  • 身内だけに見せられる。 数人に URL と合言葉を渡し、それ以外には閉じておく
  • ただ乗りされない。 画面にも API にも、合言葉なしでは届かない

しくみは Basic認証の記事 と同じものになる。

functions/_middleware.js をルート直下に置くと、サイトへの全リクエストに割り込む。 静的ファイルも含めて、すべてがいったんここを通る。ここで合言葉を確かめてしまえば、通らないかぎり先へ進めない。

Claude
このサイト全体に Basic 認証をかけてください。
- functions/_middleware.js をルート直下に置く
- 合言葉は Secret の SITE_PASSWORD と照合する
- SITE_PASSWORD が設定されていないときは、誰も通さない
- パスによる出し分けはしない。すべてのURLを対象にする

後半の2行に理由がある。

未設定なら誰も通さない。 設定を忘れたときに「誰でも入れる」ではなく「誰も入れない」に倒れる。公開した瞬間に無防備になる時間を作らないための備え。

パスで出し分けない。 「トップページだけ」「/secret/ の下だけ」のように範囲を決めると、URL の書き方がぶれたときに判定をすり抜けられるBasic認証の記事 では、//secret/ のようにスラッシュを重ねるだけで鍵をすり抜けられる例を扱った。あちらは「一部だけ守る」記事なので、パスを正規化して対処している。この記事は全部を対象にするので、その判定自体が要らない。

まず手元で動かす。

Claude
ローカルサーバーを起動して、動作確認したい

起動するのは wrangler pages dev(Pages Functions も一緒に動く Wrangler 内蔵のサーバー)。python -m http.server のような静的ファイルを配るだけのサーバーでは、functions/ の中身が動かないので認証も API も働かない。画面だけが出て、鍵がかかっていない状態になる。

ターミナルでやる場合: npx wrangler pages dev

表示されたURL(http://localhost:8788 など)を開くと、ブラウザが合言葉を聞いてくる。入れて中に入り、タイトル案が出れば動いている。

そのうえで、塞がっているかを確かめる。「トップページだけ守られていて、直接URLを開けば中身が見える」では意味がない。

Claude
ローカルで動いているサイトに、合言葉なしでアクセスして、
次のURLがすべて塞がっているか実際にアクセスして確かめてください。
結果を一覧で見せてください。
- トップページ、index.html、CSSファイル
- サブディレクトリとその配下の深いパス
- /api/generate
- favicon.ico
- 存在しないパス
- スラッシュを重ねたもの(//index.html など)
- ドットを含むもの(/./index.html、/sub/../index.html)

実際に確かめると、すべて 401 で返る

試したURL応答
/ /index.html /style.css401
/sub/ /sub/deep/index.html401
/api/generate401
/favicon.ico401
存在しないパス401
//index.html401
/./index.html /sub/../index.html401

画像もCSSも、深い階層も、存在しないパスも、URL の書き方を変えたものも通らない。理由は「パスを見ていない」から。 ルート直下の _middleware.js が全リクエストを受け、その入口で断っている。

合言葉を入れれば、通常どおり中に入れる。

鍵をかけると、画像やCSSのような静的ファイルも毎回サーバー側の処理を通る。 鍵がなければ、静的ファイルはそのまま配信されて処理を通らない。全部を通すようにすると、そのすべてが実行回数として数えられる。

個人で使う範囲なら無料枠に収まる。鍵をかける以上は避けられない代償として知っておく。

ローカルで動いたら公開する。初回はプロジェクトを作るところからなので、まとめて頼む。

Claude
このアプリを Cloudflare Pages に公開したい。
Pages プロジェクトがまだ無いので、作るところからお願いします。
プロジェクト名は <ベース名> で。

「デプロイして」とだけ伝えると、プロジェクトが無いと言われて止まることがある。「作るところから」まで含めると確実。

やっていることは2つ。①Pages プロジェクトを作る、②wrangler.jsoncpages_build_output_dir./public)を読んでデプロイする(functions/ の中身は自動でまとめられる)。

ターミナルでやる場合:

Terminal window
npx wrangler pages project create my-titler --production-branch main
npx wrangler pages deploy

my-titler は自分のベース名に読み替え)

デプロイが終わると、公開URL(https://<ベース名>.pages.dev)が表示される。

この時点では、まだ APIキーも合言葉も入っていない。 開いてみると、7章で「未設定なら誰も通さない」ようにしておいたので、誰も中に入れない。公開はされているのに無防備な時間はできない、という状態になる。

ローカルでは .dev.vars に置いた値を、本番にも渡す。ここだけは自分で入力する。

Terminal window
npx wrangler pages secret put ANTHROPIC_API_KEY
npx wrangler pages secret put SITE_PASSWORD

.dev.vars に書いたものと同じ値を、それぞれ貼り付ける。

入ったかどうかの確認は頼んでよい(値そのものは表示されない)。

Claude
本番に登録されている Secret の一覧を見せてください
Claude
Secret を入れたので、もう一度デプロイしてください

公開されたURLを開き、合言葉を入れてタイトル案が出れば完成になる。

9. トラブル回避と、詰まるところ

Section titled “9. トラブル回避と、詰まるところ”

返ってきた数字とメッセージで、原因が分かる。

状況HTTPメッセージの書き出し
クレジットの残高がない400Your credit balance is too low
自分で設定した上限に達した400You have reached your specified API usage limits
短時間に呼びすぎた429待つべき秒数が一緒に返る

同じ 400 でも意味が違うので、メッセージの本文を読む。上限に達したときは、上限を上げるか、翌月まで待つことになる。

9-2. 画面に出してはいけないもの

Section titled “9-2. 画面に出してはいけないもの”

Claude API から返ったエラーを、そのまま画面に出さない。内部の事情が利用者に見えてしまう。記録には残し、画面には短く返す。

console.error("Claude API エラー:", res.status, JSON.stringify(data));
return json({ error: "AI の呼び出しに失敗しました" }, 502);

作っていると増やしたくなるが、この3つは残しておく

  • max_tokens:1回あたりの最悪額を決めている
  • 入力の文字数制限:長文を投げ込まれると、そのまま入力側の課金になる
  • モデルの固定:利用者からモデル名を受け取ると、勝手に高いモデルを指定される

9-4. キーをうっかり公開してしまったら

Section titled “9-4. キーをうっかり公開してしまったら”

まずキーを無効化する。 履歴から消す作業より先にやる。コンソールでそのキーを削除し、新しいキーを作り直せばよい。消したキーは、拾われていてももう使えない。

このとき、4章で上限を設定してあれば、被害額はその範囲で止まっている

9-5. 使った額を自分で見えるようにしておく

Section titled “9-5. 使った額を自分で見えるようにしておく”

作例では、結果の下に使ったトークン数を出している。

入力 213 トークン / 出力 336 トークン

自分のアプリが1回いくら使っているのかが、使うたびに見える。 コンソールの使用額もほぼすぐ反映されるので、突き合わせて確かめられる。

公開したら、別の新しいセッションを開いて点検を頼む。手順は 公開したWebアプリのセキュリティを点検する にある。

その記事で聞く4項目のうち、この作例は2つに直接あてはまる

  • 認証なしで叩ける API があるか(とくに AI を呼ぶもの)
  • 課金が発生するものが無防備に置かれていないか

つまり、この記事で作ったものは、点検の対象そのものになる。作った本人ではないセッションから試してもらって、合言葉なしでは何も見えないことを確かめる。

練習で作ったものを置きっぱなしにしない。動いているものは、費用が発生しうるものでもある。

  • APIキーを削除する。 期限を付けてあれば自然に切れるが、待たずに消してよい
  • ワークスペースをアーカイブする。 記録は残したまま、そこのキーがまとめて使えなくなる
  • Pages のプロジェクトを消す。 npx wrangler pages project delete <ベース名> で消える

残高が残っているクレジットは、次に使うときまでそのまま置いておける(購入から1年で失効する)。

  • サブスクと API が別の財布であることを、請求の形で理解した
  • 上限を先に設定してから作り始めた。青天井を自分で閉じた
  • モデルを、カタログではなく実測で選んだ。しかもその根拠を残した
  • 秘密をコードから外に出し、ローカルと本番で別々に渡した
  • 公開URL全体に鍵をかけた。作りかけを置いても、ただ乗りされない

この形は、Claude に限らず他社の API を使うときにもそのまま応用できる。上限を先に決めて、キーをサーバー側に置き、公開URLに鍵をかけて、公開後に点検する。順番は変わらない。