第6章 公開タイムライン — 「自分だけ」と「みんな」をRLSで両立

📖 この章のゴール:「自分だけ」と「みんな(公開)」を、RLSで両立できるようになる。第4章で作った「自分の行だけ」の鍵を、「公開のもの or 自分のもの」へと広げます。 ← 目次・はじめにへもどる


📱 Twitterではこう見える

ホーム(タイムライン)を開くと、他人の投稿が新しい順に流れてきます。 自分のものだけが見えるわけではありません。

  • 公開ツイートは、フォローしていなくても基本だれでも読める(=みんなに見せる)
  • 一方で、下書き(まだ公開していない投稿)は、自分にしか見えない
  • 鍵アカウントの投稿も、許された人だけ

つまりTwitterは、「みんなに見せるもの」と「自分だけのもの」が、同じ画面・同じ仕組みの中に混ざっています。

そして第4章と同じで、この「公開/非公開の出し分け」も、作る人がちゃんと設定して初めて成り立ちます。 サボると、下書きまで世界中に丸見え——という事故になります。


🤔 「自分だけ」と「みんな」をどう両立する?(🟢 基礎)

第4章で書いた「読む」ルールを思い出してください。

using ( auth.uid() = user_id )自分の行だけを読める

これは強い守りでした。でも、これだと 他人のツイートは1件も読めません。 タイムラインに他人の公開ツイートが流れてこない——Twitterになりません。

かといって「全部読めるようにする」と、今度は 他人の下書きまで見えてしまいます。 どちらも極端でダメ。ちょうど良い真ん中が欲しいのです。

🗒 たとえ話:1冊のノートに「掲示板」と「日記」が混ざっている 同じノートの中に、みんなに読んでほしいページ(掲示板)と、自分しか読まない日記が混在しているとします。 ページの表紙に「公開」シールか「ひみつ」シールを貼っておけば、こうルールを決められます—— 「『公開』シールのページは誰でも読める。シールが無い(=ひみつ)ページは、書いた本人だけ読める。

Webでも、やることはこのシールと同じ2つです。

  1. ツイート1件ごとに「公開フラグ」を付けるis_public(公開なら true、下書きなら false
  2. 読むルールを「公開のもの 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_publictrue(=公開)の行だけ」にしぼる(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_publictrue の行=公開だけが読める、という形で効きます。)


⚠️ ハマりどころ

  • is_public の既定を意図と逆にするdefault true(公開)か default false(非公開)か、アプリの性格に合っているかを必ず確認。逆にすると「下書きのつもりが全公開」「公開したつもりが誰にも見えない」事故に。
  • 読むルールを using ( true ) で全開放する … 「みんなに見せたい」からと true にすると、他人の下書きまで丸見えです。必ず is_public or auth.uid() = user_id の形に。
  • 下書きのつもりが、公開のままinsertis_public: false書き忘れると、default true で公開されます。下書きは明示的に false を。
  • 更新後に is_public を確かめない … 「公開→下書きに戻す」操作のあと、ほんとうに他人から見えなくなったかを、別アカウント(または未ログイン)で必ず確認。

🤖 AIに頼むなら(Vibe codingのコツ)

「読むルールを広げる」のは、うっかり全開放(using ( true ))にされがちな所です。だから条件を明示して頼みます。

🗣 プロンプト例: 「tweetsis_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章 フォロー。「人と人を結ぶ」を作ります。

← 目次・はじめにへもどる