Rork で Supabase を使ったアプリを作ったのに、データが一向に表示されありません。そのパターン、私も何度か経験しました。
厄介なのは、エラーメッセージが出ないこともあることです。画面は正常に表示されているのに、リストが空のまま。コンソールを見ても undefined が返ってきているだけ。「Supabase の設定は合ってるはずなのに…」と途方に暮れる感覚は、初心者にとってかなり心が折れます。
ここではよくある原因を4つのパターンに絞り込んで、それぞれの診断方法と対処法を整理しています。順番に確認していただくと、多くのケースで原因を特定できると思います。
パターン1: RLS(Row Level Security)が原因 — 最も多い落とし穴
Supabase でデータが取得できないとき、原因の半分以上は RLS(行レベルセキュリティ)の設定にあります。
RLS を有効にすると、デフォルトではすべてのアクセスが拒否されます。「テーブルにデータは入っているはずなのに取れない」という状況のほとんどが、これが原因です。
診断方法
Supabase ダッシュボードの「Table Editor」を開き、テーブルを直接クリックしてみてください。データが表示される場合、アプリ側の認証状態と RLS ポリシーのズレが原因です。
次に「Authentication → Policies」でテーブルのポリシーを確認します。
-- こういう状態のとき、データが取れません
-- ポリシー: なし(有効: RLS ON)
-- すべてのユーザーからのアクセスが拒否されます
-- 開発中の一時的な確認用(本番では使わない)
create policy "Allow all for testing"
on your_table
for select
using (true);ログイン済みユーザーにだけデータを返す正しいポリシー
-- ユーザー自身のデータだけを読めるようにする(一般的なパターン)
create policy "Users can read their own data"
on your_table
for select
using (auth.uid() = user_id);ポイントは auth.uid() です。これは Supabase が提供する関数で、現在ログイン中のユーザーの ID を返します。テーブルの user_id カラムと一致するレコードだけが返ってきます。
ログイン前にデータを取得しようとしていませんか?
アプリ起動直後、まだログインが完了していない状態でデータを取得しようとすると、RLS によってすべて弾かれます。認証状態の確認を必ず先に行いましょう。
// ❌ 問題のあるパターン(認証確認なしでデータ取得)
useEffect(() => {
fetchData(); // ログイン前に実行される可能性がある
}, []);
// ✅ 認証後にだけ取得するパターン
useEffect(() => {
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
if (session) {
fetchData(); // ログイン済みのときだけ実行
}
}
);
return () => subscription.unsubscribe();
}, []);パターン2: Supabase の URL・API キーの設定ミス
意外と多いのが、.env ファイルの設定ミスです。特に複数のプロジェクトを同時に進めているときに起きやすいです。
確認すべきこと
// supabase.ts(または supabase/client.ts)
import { createClient } from '@supabase/supabase-js';
const supabaseUrl = process.env.EXPO_PUBLIC_SUPABASE_URL\!;
const supabaseAnonKey = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY\!;
// 開発中はこのログで確認できます
console.log('Supabase URL:', supabaseUrl?.substring(0, 30) + '...');
export const supabase = createClient(supabaseUrl, supabaseAnonKey);Expo(React Native)の場合、環境変数は EXPO_PUBLIC_ で始まる必要があります。この接頭辞がないと、クライアント側のコードから変数にアクセスできません。
Supabase ダッシュボードの「Settings → API」で確認できる値と、アプリが使っている値が一致しているか確認してください。特に「anon key」と「service_role key」を間違えないよう注意が必要です(service_role key はサーバーサイドでのみ使うものです)。
Rork での環境変数設定については、Rork の環境変数・設定ミスのトラブルシューティングも参考にしてみてください。
パターン3: 非同期処理の実装ミスでデータが空になる
RLS も API キーも問題なさそうなのにデータが表示されない場合、JavaScript の非同期処理の扱いに問題があるケースです。
よくある間違いパターン
// ❌ async/await が正しく使えていない例
const fetchData = () => {
const { data, error } = supabase
.from('posts')
.select('*');
// Promiseをawaitしていないため、dataはundefinedのまま
setData(data);
};
// ✅ 正しい非同期処理
const fetchData = async () => {
const { data, error } = await supabase
.from('posts')
.select('*');
if (error) {
console.error('取得エラー:', error.message);
return;
}
setData(data ?? []); // nullの場合は空配列にする
};await を忘れるのは本当によくある間違いです。Promise は即座に値を返さないので、await なしで data を参照すると undefined になります。
React のレンダリングタイミングの問題
// ❌ コンポーネントがマウントされる前に状態が設定される可能性
const MyComponent = () => {
const [posts, setPosts] = useState(null); // ← null で初期化
fetchData(); // useEffect外で呼び出している
return <View>{posts.map(...)}</View>; // nullのときにクラッシュ
};
// ✅ 正しいパターン
const MyComponent = () => {
const [posts, setPosts] = useState<Post[]>([]); // 空配列で初期化
const [loading, setLoading] = useState(true);
useEffect(() => {
const fetch = async () => {
setLoading(true);
const { data, error } = await supabase.from('posts').select('*');
if (\!error && data) setPosts(data);
setLoading(false);
};
fetch();
}, []);
if (loading) return <ActivityIndicator />;
if (posts.length === 0) return <Text>データがありません</Text>;
return <FlatList data={posts} renderItem={...} />;
};初期値を null にすると .map() を呼び出したときにクラッシュします。配列を扱う場合は必ず空配列 [] で初期化するのが安全です。
パターン4: テーブル名・カラム名の間違い
地味ですが、これも頻繁に起きます。Supabase のテーブル名はデフォルトでスネークケース(user_profiles)ですが、コード側でキャメルケース(userProfiles)を使ってしまうケースです。
// ❌ テーブル名・カラム名が違う
const { data } = await supabase
.from('userProfiles') // ← Supabase側はuser_profilesかもしれない
.select('userName'); // ← Supabase側はuser_nameかもしれない
// ✅ ダッシュボードで確認した正確な名前を使う
const { data } = await supabase
.from('user_profiles') // ← ダッシュボードで確認
.select('user_name, avatar_url, created_at');Supabase ダッシュボードの「Table Editor」でテーブル名とカラム名を正確に確認してから書くようにしましょう。
また、select('*') で全カラム取得してから、返ってくるデータの形を console.log で確認するのが一番手っ取り早いです。
診断フロー — 迷ったらこの順番で確認
まとめると、データが取得できないときは次の順番で確認するのが効率的です。
まずは console.log(error) を追加して、エラーメッセージを確認してください。エラーが null で data も null の場合は、RLS が原因である可能性が高いです。エラーに「Invalid API key」が含まれる場合は API キーの問題です。
次に Supabase ダッシュボードの「Logs → API Logs」を確認します。リアルタイムでどんなクエリが飛んでいるか、ステータスコードが何かを確認できます。406 が返っている場合は RLS によるアクセス拒否が疑われます。
データベース接続エラーの詳細なトラブルシューティングや、Supabase の認証エラーを解決する方法も合わせて参考にしてみてください。
次のステップ
データが取得できるようになったら、次は実際にアプリとして動くかを実機でテストしてみましょう。シミュレーターでは再現しない問題が実機で出ることもあります。
Supabase の RLS は最初は難しく感じるかもしれませんが、一度仕組みを理解すると、細かいアクセス制御ができる強力な機能として活用できます。まずは開発中は一時的にすべてのアクセスを許可するポリシーを設定して動作確認し、リリース前に適切なポリシーに絞っていくアプローチがおすすめです。