第6章 公開タイムライン — 「自分だけ」と「みんな」をRLSで両立
📖 この章のゴール:「自分だけ」と「みんな(公開)」を、RLSで両立できるようになる。第4章で作った「自分の行だけ」の鍵を、「公開のもの or 自分のもの」へと広げます。 ← 目次・はじめにへもどる
📱 Twitterではこう見える
ホーム(タイムライン)を開くと、他人の投稿が新しい順に流れてきます。 自分のものだけが見えるわけではありません。
- 公開ツイートは、フォローしていなくても基本だれでも読める(=みんなに見せる)
- 一方で、下書き(まだ公開していない投稿)は、自分にしか見えない
- 鍵アカウントの投稿も、許された人だけ
つまりTwitterは、「みんなに見せるもの」と「自分だけのもの」が、同じ画面・同じ仕組みの中に混ざっています。
そして第4章と同じで、この「公開/非公開の出し分け」も、作る人がちゃんと設定して初めて成り立ちます。 サボると、下書きまで世界中に丸見え——という事故になります。
🤔 「自分だけ」と「みんな」をどう両立する?(🟢 基礎)
第4章で書いた「読む」ルールを思い出してください。
using ( auth.uid() = user_id )… 自分の行だけを読める
これは強い守りでした。でも、これだと 他人のツイートは1件も読めません。 タイムラインに他人の公開ツイートが流れてこない——Twitterになりません。
かといって「全部読めるようにする」と、今度は 他人の下書きまで見えてしまいます。 どちらも極端でダメ。ちょうど良い真ん中が欲しいのです。
🗒 たとえ話:1冊のノートに「掲示板」と「日記」が混ざっている 同じノートの中に、みんなに読んでほしいページ(掲示板)と、自分しか読まない日記が混在しているとします。 ページの表紙に「公開」シールか「ひみつ」シールを貼っておけば、こうルールを決められます—— 「『公開』シールのページは誰でも読める。シールが無い(=ひみつ)ページは、書いた本人だけ読める。」
Webでも、やることはこのシールと同じ2つです。
- ツイート1件ごとに「公開フラグ」を付ける …
is_public(公開ならtrue、下書きならfalse) - 読むルールを「公開のもの or 自分のもの」に広げる … RLSのポリシーに OR(または) を入れる
この章では、第4章のテーブルに is_public という列を1つ足し、読むポリシーを書き換えるだけで、公開タイムラインが作れます。
🛠 Supabaseでこう作る(🟢 基礎)
① ツイートに「公開フラグ」の列を足す
第4章で作った tweets テーブルに、公開か下書きかを表す列を1つ追加します。
alter table tweets add column is_public boolean not null default true;
1行ずつ読むと:
alter table tweets… 「tweets(ツイート)表を 作り変える」という宣言(alter= 変更する)。add column is_public… 「is_public(公開かどうか)という 新しい列を足す」。boolean… 中身は true / false の2択(boolean = はい/いいえの値)。true= 公開、false= 下書き。not null… 空はダメ。必ず公開か下書きか、どちらかに決める。default true… 何も指定せず投稿したら、自動でtrue(公開)になる。
🐦 なぜ「既定で公開」にするの? Twitterは「ふつうに投稿したら、みんなに見える」のが基本だからです。「下書きにしたいときだけ、自分で
falseを指定する」——という設計にしています。(もしSNSの性格を「既定で非公開」にしたいなら、default falseにすればOKです。)
② 読むポリシーを「公開 or 自分」に置き換える
第4章で作った「自分のツイートだけ読む」ルールを、一度消して、広げたルールに作り直します。
-- 第4章で作った「自分だけ」の読むルールを、いったん消す
drop policy "自分のツイートを読む" on tweets;
-- 「公開のもの or 自分のもの」を読めるルールを作り直す
create policy "公開か自分のツイートを読む" on tweets
for select using ( is_public or auth.uid() = user_id );
1行ずつ読むと:
drop policy "自分のツイートを読む" on tweets;… 第4章の読むルールを 削除(drop= 捨てる)。同じ操作(読む=select)のルールが2つあると混乱するので、まず古い方を外す。create policy "公開か自分のツイートを読む"… 新しい読むルールを作る(名前は自由)。for select… 「読む」操作のときのルール(select= 読む)。using ( is_public or auth.uid() = user_id )… 通す条件。is_public(その行が公開)または、auth.uid() = user_id(その行の持ち主が自分)なら読める。
ここの or(または) が、この章の主役です。「公開なら誰でも」と「自分のなら下書きでも」の 両方を1本のルールで叶えています。
| 条件 | これが true だと… |
|---|---|
is_public |
その行は 公開 → だれでも読める |
auth.uid() = user_id |
その行の持ち主は 自分 → 自分の 下書きも読める |
is_public **or** auth.uid() = user_id |
どちらか一方でも当てはまれば、その行を読める |
🔒 作る・直す・消す(insert/update/delete)は第4章のままでOKです。書き換えるのは「読む(select)」だけ。投稿や削除は今までどおり「自分の分だけ」に守られています。
③ 公開タイムラインを取得する(ブラウザ側)
画面側のコードは、第4章・第5章とほとんど同じです。
// タイムラインちょうだい(新しい順)
const { data } = await supabase
.from('tweets')
.select('*')
.order('created_at', { ascending: false });
1行ずつ読むと:
supabase.from('tweets')… 「tweets表に用がある」と伝える。.select('*')… 全部の列をちょうだい(*= 全部)。.order('created_at', { ascending: false })… 投稿時刻で 新しい順に並べて(ascending: false= 降順)。
注目してください。コードは前章とまったく同じなのに、②でルールを広げたおかげで、返ってくる中身が変わります。 いまや返るのは「世界中の公開ツイート + 自分の下書き」です。これがタイムラインの素になります。
もし「公開ツイートだけ」を取りたい(=自分の下書きは混ぜたくない)なら、条件を1つ足すだけです。
const { data } = await supabase
.from('tweets')
.select('*')
.eq('is_public', true) // 公開のものだけに絞る
.order('created_at', { ascending: false });
.eq('is_public', true)… 「is_publicがtrue(=公開)の行だけ」にしぼる(eq= イコール)。
④ 下書きとして投稿する
公開ではなく 下書きで保存したいときは、投稿時に is_public: false を渡します。
// 下書き(自分にしか見えない)として保存
await supabase.from('tweets').insert({ body: '下書き', is_public: false });
1行ずつ読むと:
.insert({ ... })… 1件 追加する(Create)。body: '下書き'… 本文。is_public: false… これをfalseにするから下書きになる。書かなければ①のdefault trueが効いて公開になる点に注意。
🔧 補足:ログアウト中(匿名)でも公開は読めるべき
タイムラインの公開ツイートは、ログインしていない人(匿名のお客さん)にも見えるのが自然です(Twitterも未ログインで公開ツイートが読めます)。
Supabaseでは、ログイン中の人は authenticated(認証済み)、ログインしていない人は anon(アノン=匿名) という役割で扱われます。匿名でも公開ツイートを読めるようにするには、anon にも「読む(select)」を許す設定が要ります——とだけ、ここでは押さえておいてください。(ポリシーの using ( is_public or ... ) 自体は、匿名なら auth.uid() が空になり、結局 is_public が true の行=公開だけが読める、という形で効きます。)
⚠️ ハマりどころ
is_publicの既定を意図と逆にする …default true(公開)かdefault false(非公開)か、アプリの性格に合っているかを必ず確認。逆にすると「下書きのつもりが全公開」「公開したつもりが誰にも見えない」事故に。- 読むルールを
using ( true )で全開放する … 「みんなに見せたい」からとtrueにすると、他人の下書きまで丸見えです。必ずis_public or auth.uid() = user_idの形に。 - 下書きのつもりが、公開のまま …
insertでis_public: falseを 書き忘れると、default trueで公開されます。下書きは明示的にfalseを。 - 更新後に
is_publicを確かめない … 「公開→下書きに戻す」操作のあと、ほんとうに他人から見えなくなったかを、別アカウント(または未ログイン)で必ず確認。
🤖 AIに頼むなら(Vibe codingのコツ)
「読むルールを広げる」のは、うっかり全開放(using ( true ))にされがちな所です。だから条件を明示して頼みます。
🗣 プロンプト例: 「
tweetsにis_public(boolean, default true) を足して、公開/下書きを分けて。読む(select)ポリシーはis_public or auth.uid() = user_idにして、第4章のinsert/update/delete(自分の行だけ)はそのまま残して。匿名(anon)でも公開ツイートは読めるようにして」
出てきたコードは、この2点を別アカウント(または未ログイン)で必ずチェック:
- ✅ 下書き(
is_public: false)が、他人には見えない?(自分だけに見える?) - ✅ 公開ツイートが、未ログイン(匿名)でも見える?
この2つが両立して初めて「公開タイムライン」は完成です。
📝 ことばメモ
- 公開/非公開:その投稿を「みんなに見せる」か「自分だけにする」か
is_public:公開かどうかを表す true/false の列(true= 公開、false= 下書き)- ポリシーのOR条件:「公開 または 自分」のように、どちらか一方でも当てはまれば通す書き方
- 匿名(anon):ログインしていない人を指す役割。公開データだけが読める
➡️ 次の章へ
ここまでで「自分だけ」と「みんな(公開)」を、RLSの OR条件ひとつで両立できました。次の第7章では、その中間にある「フォローした人の投稿だけ」を作るために、人と人を結ぶ仕組み(フォロー関係)に進みます。「みんな」でも「自分だけ」でもない、つながりで決まる見え方がテーマです。
次は 第7章 フォロー。「人と人を結ぶ」を作ります。