第5章 APIって何? — データの出し入れ窓口を「設計」する
📖 この章のゴール:API(窓口)とは何か、操作の基本形 CRUD、そして 「同じ窓口でも、人によって返る中身が違う(出し分け)」 の正体が分かる。 ← 目次・はじめにへもどる
📱 Twitterではこう見える
- 同じ「ホーム」を開いても、人によって出てくる投稿が違う。
- できる操作は決まっている(投稿する・消す・フォローする…)。「好きにDBを書き換える」ことはできない。
- これらは全部、決まった窓口(API)を通して行われています。
第1章で「API=注文窓口」と説明しました。この章では、その窓口を自分で考える(設計する)目線に立ちます。
🤔 APIって何だっけ?(🟢 基礎)
第1章のおさらい+もう一歩。
API = ブラウザとサーバー(DB)をつなぐ「決まった注文窓口」であり、「できる注文の一覧(メニュー)」でもあります。
レストランで、お客さんはメニューに載っている注文しかできません。厨房(DB)に勝手に入って料理することはできない。これは不便なのではなく、安全と整理のための仕組みです。
だから——
「APIを設計する」=「どんな注文(操作)を受け付けるか、メニューを決める」こと。
メニューに無い操作はできない。これが「なんでもアリ」を防ぎ、アプリを安全に保ちます。
🤔 操作の基本は4つ「CRUD」(🟢 基礎)
データに対してできることは、つきつめると たった4つです。
| やりたいこと | 呼び方 | SQL/操作 | Twitterでの例 |
|---|---|---|---|
| 作る | Create | insert |
ツイートする |
| 読む | Read | select |
タイムラインを見る |
| 直す | Update | update |
プロフィールを編集する |
| 消す | Delete | delete |
ツイートを削除する |
頭文字を取って CRUD(クラッド) と呼びます。どんなアプリも、だいたいこの4つの組み合わせでできています。
🔁 思い出してください。第3章で
insert(作る)とselect(読む)をやりました。第4章のRLSでは、この4操作のそれぞれにルール(select/insert/update/deleteポリシー)をかけましたね。CRUDは、第4章で守った4つの操作そのものです。
🤔 「ユーザーごとに出し分ける」の正体(🟢 → 🔧)
ここが、この章のいちばん面白いところです。
AさんとBさんが、まったく同じ「自分のツイート一覧」窓口を叩く。 なのに、Aさんには Aさんの投稿、Bさんには Bさんの投稿が返る。
なぜ、同じ窓口なのに中身が違うのでしょう?
答えは——「誰が叩いたか」が違うからです。
Aさんの注文 ─┐ ┌─→ Aさんのデータが返る
├─→ 同じAPI(窓口)─→ RLS ┤
Bさんの注文 ─┘ +「誰が叩いたか」 └─→ Bさんのデータが返る
- 窓口(API)は1つ。
- でもリクエストには 「いまログインしている人のID」(=第2章のトークン) がくっついている。
- DB側の RLS(第4章) が、そのIDを見て 「あなたの行だけ」 を返す。
✅ だから「出し分け」の正体は = 「同じAPI」+「ログイン中の人のID」+「権限ルール(RLS)」。 一覧APIに毎回「
user_id = 自分」と書かなくても、ログインIDから自動で出し分けられる——これは第4章「DB側で守る強さ」の言いかえです。
🛠 Supabaseでこう作る(🟢 基礎)
うれしい事実から。
Supabaseは、テーブルを作ると “窓口(API)” を自動で用意してくれます。 あなたがAPIをイチから書く必要はありません。
APIには「2つの顔」がある(🔧)
- Supabaseクライアント(書きやすい顔) … JSから
supabase.from('tweets').select()のように書く。第3章で使ったのがこれ。 - その下で動く REST API(HTTPの素の顔) … クライアントは、この「素のHTTP」への言いやすい言い方にすぎません。
裏側では、だいたいこんなHTTPリクエストが飛んでいます(イメージ)。
GET https://<あなたのプロジェクト>.supabase.co/rest/v1/tweets?order=created_at.desc
Authorization: Bearer <あなたのログイン・トークン> ← これで「誰が叩いたか」が伝わる
GET .../tweets… 「tweetsを読む」窓口(エンドポイント)。?order=created_at.desc… 「新しい順で」という注文の指定。Authorization: Bearer <トークン>… 第2章のログイン証明書。これがあるから、サーバーは「誰か」を知り、RLSが効いて出し分けられるのです。
CRUDをSupabaseクライアントで
// Create(作る):投稿する
await supabase.from('tweets').insert({ body: 'やあ' });
// Read(読む):新しい順で一覧
await supabase.from('tweets').select('*').order('created_at', { ascending: false });
// Update(直す):このidのツイートを直す
await supabase.from('tweets').update({ body: 'なおした' }).eq('id', 123);
// Delete(消す):このidのツイートを消す
await supabase.from('tweets').delete().eq('id', 123);
1行ずつのポイント:
.insert({ ... })… 1行 追加(Create)。.select('*')… 取り出す(Read)。*は全部の列。.update({ ... }).eq('id', 123)… 「idが 123 の行」を直す(Update)。.eqは「=(イコール)」で対象をしぼる。.delete().eq('id', 123)… 「idが 123 の行」を消す(Delete)。
🔒 ここで第4章が効きます。たとえ 他人の
idを指定してupdate/deleteを叩いても、RLSが「自分の行じゃない」と弾きます。つまり「出し分け(自分の分だけ)」と「安全(他人を触れない)」が、同じRLSの仕組みで同時に実現しています。
⚠️ ハマりどころ
- 「APIを全部自分で書かなきゃ」と思う … Supabaseが自動で用意します。まずはクライアントから使う。
- 出し分けをフロントの
ifで頑張る … 「自分のじゃないから隠す」を画面側だけでやると、窓口を直接叩かれたら漏れます。出し分けと安全は RLS(サーバー側) で。画面のifは見た目専用。 - トークンを付けずに(匿名で)叩く … 「誰か」が伝わらず、公開データしか取れない/何も取れない。ログイン状態を確認。
- なんでもできる窓口にしてしまう … メニュー(操作)は必要な分だけ。権限が広すぎるAPIは事故のもと(最小権限)。
🤖 AIに頼むなら(Vibe codingのコツ)
🗣 プロンプト例: 「
tweetsの CRUD(作る・読む・直す・消す) をSupabaseクライアントで書いて。出し分け(自分の投稿だけ・他人のは触れない)はRLSでやり、フロントのifには頼らないで。読みは新しい順で」
出てきたコードのチェックポイント:
update/deleteが 持ち主チェック(RLS) で守られている?(フロントのifだけになっていないか)- クライアントが ログイン状態で叩いている?(匿名になっていないか)
- 受け付ける操作が必要な分だけに絞られている?
📝 ことばメモ
- API:決まった注文窓口(メニュー)。外から使える操作の入口
- CRUD:Create / Read / Update / Delete = 作る・読む・直す・消す
- エンドポイント:窓口ひとつひとつ(URL)。例
/rest/v1/tweets - REST:HTTPでデータを操作する、定番の窓口の作り方
- 出し分け:同じ窓口でも、ログインしている人によって返る中身が違うこと(正体 = ID + RLS)
- Bearer トークン:「私はこの人です」とリクエストに添える証明書(第2章のログイン証明書)
➡️ 次の章へ
ここまでは「自分のものは自分だけ」でした。次の第6章では、「公開ツイートは “みんな” に見せる」というルールをRLSに足して、公開タイムライン(全員の投稿が新しい順に流れる画面)を作ります。「自分だけ」と「みんな」を、どう両立させるかがテーマです。