Supabaseのselect結果がちょうど1,000行で終わっても、それだけでテーブル全体が1,000行だとは判断できません。Supabaseの公式JavaScript referenceは、プロジェクトのselect応答が既定で最大1,000行で、Project API Settingsから設定を変更できること、また全件取得にはrange()でページングできることを説明しています。ただし、実際の上限はプロジェクト設定やAPI経路によります。まずcount・Content-Range・filter・RLSを切り分け、全件が必要なときだけ安定した順序でページをつなぎ、最後に件数とIDを照合してください。
目次
まず「1,000件で止まった」の意味を確かめる
症状だけで原因を決めず、同じ条件で次の情報を集めます。
-
クライアントが受け取った行数
-
Queryのfilter、sort、選択列
-
応答で取得できるContent-Rangeまたはcount
-
Project API Settingsに設定されている最大返却行
-
SQL Editorなど別経路で見た件数(同じschema・filter・roleに揃えた比較か)
-
実行ユーザーとRow Level Security(RLS)policy
1,000行が上限と一致しても、Max Rowsが原因と確定するのは、現在のプロジェクト設定と同じquery条件で確認できた場合です。filterが900行に絞っていれば応答は900行です。RLSで閲覧可能な行が制限されている場合も、APIから見える集合と管理者のSQL結果は異なります。逆に、画面表示のページサイズが1,000でも、queryが続きの範囲を要求しているかもしれません。アプリのUI表示件数と、HTTP応答の行数・totalは別々に記録します。
原因を順に切り分ける
1. Queryと比較対象を固定する
確認作業の前に、対象テーブルまたはview、schema、select列、filter、order、利用role、取得時刻を記録します。同じ内容を比較しないと、件数差がpaginationなのか条件差なのか分かりません。たとえば「管理画面には1,240行」とだけ記録しても、管理画面が削除済み行や別schemaを含むなら比較には使えません。
比較用に、取得時刻と実行ユーザーを含むRun IDを作り、query条件を保存します。個人情報やtokenを含むURL・request headerは監査ログへそのまま書かず、filterの安全な表現やsecretを除いた条件を記録します。
2. Max Rowsの実設定を確認する
SupabaseのJavaScript referenceは、既定の最大行数とProject API Settingsでの変更を案内しています。設定を確認するときは、本番と開発projectを取り違えず、対象projectのAPI設定を確認します。設定値を上げる前に、そのアプリが本当に全件を同時に必要とするか、応答サイズ、処理時間、アクセス制御を検討してください。
単に上限を大きくすれば、全件取得の問題が解決するとは限りません。データが増えれば、さらに大きい応答が必要になります。また、RLSやfilterの問題、UI側の表示制限、途中で終了するクライアント処理はMax Rowsを変えても直りません。大量データを一度に返さず、ページ単位で処理する方が適切な場面もあります。
3. 件数ヘッダーを読む
PostgRESTのpagination資料では、応答のContent-Rangeが返された範囲を示し、countを要求した場合は総数も含められると説明されています。たとえば0-999/2400なら、範囲0〜999の1,000行を返し、条件に合う総件数として2,400が示された状態です。0-999/のようにtotalがなら、総数はその応答で確認できていません。ヘッダーがアプリから読めない場合は、ブラウザのNetwork、SDKのresponse metadata、サーバーログなど、使用する経路で確認できる場所を調べます。
Supabase JavaScriptのselect referenceは、count: exactとhead: trueを使った件数取得例を示しています。大きなテーブルではexact countの負荷を考慮し、必要な頻度・方法を選びます。PostgRESTはexact、planned、estimatedなどのcount方法を説明しています。plannedは統計に基づく推定で、正確な実数として扱わないでください。countが取得できない、あるいは推定値だけの場合は「総件数との照合不可」と記録します。
4. FilterとRLSを確認する
件数差があるときは、同じfilterを両方の経路へ適用したか確認します。たとえばstatus = active、期間条件、tenant ID、soft delete列の条件が片方だけにあると、paginationではない差が生まれます。RLSを無効化したり、広い権限のkeyへ置き換えて診断するのではなく、対象roleで何が見えるかを確認してください。
最初の1,000件に同じsort条件が適用されていなかったり、複数ページでfilter値が変化したりすると、ページを足し合わせた集合は信頼できません。filter、order、tenant・user scopeをページ間で変えない設計にします。
range()でページングする
Supabase JavaScript clientにはrange(from, to)を使う例があります。範囲のfrom/toは0始まりで、両端を含みます。たとえば1,000行ずつ取得する単純な例は次の形です。
const pageSize = 1000
let from = 0
const allRows = []
while (true) {
const to = from + pageSize - 1
const { data, error } = await supabase
.from('records')
.select('id, created_at, status', { count: 'exact' })
.order('created_at', { ascending: true })
.order('id', { ascending: true })
.range(from, to)
if (error) throw error
const page = data ?? []
allRows.push(...page)
if (page.length < pageSize) break
from += pageSize
}
これは設計の骨組みです。実際のSDK version、API上限、RLS、query、データ量に合わせて修正し、テストしてください。count: exactを毎ページ要求する必要があるとは限りません。最初に一度だけ取得するか、responseのContent-Rangeを使うか、総数が高コストなら別の検証を選ぶかを決めます。コード例はこのタスクで実行したものではなく、実データでの成功を証明しません。
ページサイズを1,000に固定しないことも重要です。projectの設定上限より大きいrangeを要求しても、期待どおりの行数が一回で返るとは限りません。実際の応答件数と範囲を確認します。APIの設定値を変更する場合は、対象環境・権限・応答負荷をレビューしてから実施します。
offset型ページングの欠落リスクを減らす
range()は開始位置と終了位置で範囲を指定します。ページの間に新規行が追加・削除されると、offsetの位置が動き、同じ行の重複や未取得が起きることがあります。ページング中にデータが変わらないsnapshotが必ず保証される、と前提にしないでください。
最低限、並び順には一意なtie-breakerを付けます。created_atだけでは同時刻の行が複数あるため、最後に一意なidを加えます。さらに、取得を開始する時点の上限IDや時刻を決められるなら範囲を固定し、処理中に増えた行を次回へ回す方法を検討します。更新・削除が頻繁で同一snapshotが必要なら、offset pageを連続して読む方式が要件を満たすか、DB側のsnapshotやkeyset paginationを含めて別途設計します。
ページごとに、範囲、返却件数、最初と最後のID、next offset、取得時刻、重複ID数、エラー状態を保存します。生の全行や機密データをログに複製する必要はありません。障害時には最後に確定したpage境界からやり直せるようにします。再試行したpageを単純に配列へappendすると重複し得るため、IDを使って重複を検出し、重複を無言で上書きしないでください。
1,000件境界での検証表
確認|例|判定
API応答行数|1,000行|上限と一致する症状。原因はまだ未確定
Content-Range|0-999/2400|1,000行を返し、同条件のtotalが2,400
次のrange|1000-1999|前ページの終端と重ならず、連続範囲か確認
最終range|2000-2399|totalが信頼できるなら最終件数と照合
RLS/filter|APIとSQLでrole・filter一致|条件差を除いて比較
重複・欠落|ページ横断ID集合|duplicate 0、期待範囲とのgapを説明できる
concurrent update|作業中に行が変化|snapshot保証がなければ完全一致を断定しない
この例では、total=2,400が同じfilter・roleから得られる正確なcountだと確認できた場合、返却範囲を0–999、1000–1999、2000–2399に分けて検証できます。countがplanned/estimatedだったり、アクセス可能行だけの件数だったりする場合は、その意味を台帳に明記します。固定数値を業務データの期待件数として流用せず、自分のqueryで観測した結果を記録してください。
ページングの失敗パターン
-
常に1,000件で終了する:上限設定、クライアント側のloop停止条件、responseの切り捨てを確認する。
-
1,000件より少ない:filter、RLS、条件に合う行数、途中エラー、UIの表示件数を確認する。上限が原因だと決めない。
-
page 2以降で同じ行が再出現する:orderが不安定でないか、境界が重なっていないか、再試行結果をappendしていないか調べる。
-
行が飛ぶ:offset移動中のinsert/delete、filter変更、sortの非一意性を確認する。
-
countだけ大きく、データは少ない:count optionとrole/RLS、filter条件、head-only requestの違いを確認する。
-
rangeを上げても結果が増えない:project/API設定、rangeの両端、SDK version、request errorを照合する。
-
開発環境だけ動く:開発と本番のproject ref、API設定、key、schema、RLS migrationが同じか比較する。
エラーが出ていない場合も、ページ取得の完了は「要求した最終offsetまで走った」ことでは決めません。APIのtotalがあるなら同じ条件で比較し、ないなら終端・ID範囲・テストfixtureなど、確認できる根拠を列挙します。どれも利用できない場合は、全件完了ではなく「取得できた範囲」と報告するのが適切です。
実行チェックリスト
-
対象project・schema・table/view・利用roleを記録する。
-
select columns、filter、sort、実行時刻を固定する。
-
API設定のMax Rowsを確認する。必要なら対象環境を誤らず、上限変更の影響をレビューする。
-
responseの行数とContent-Range、またはSDKのcountを読み分ける。
-
APIとSQLを比べるときはfilter・権限・RLSを揃える。
-
全件取得が必要ならrange()で分割し、順序には一意IDを含める。
-
page境界、取得件数、ユニークID、duplicate、エラー・再開位置を記録する。
-
同時更新の影響とsnapshot保証を確認し、offset方式の限界を明示する。
-
期待totalがないなら、検証できない範囲をUNKNOWNとして扱う。
-
下流へ渡す前に、完全性チェックを人がreviewできるログへまとめる。
次にすること
まず一度の調査で、対象projectのMax Rows、同じroleでの正確なcount、実際のresponse metadataを確認してください。1,000行で止まったことだけでは、設定値・filter・RLSのどれが原因か決められません。全件が必要な業務処理なら、安定順のrange paginationと重複・欠落検証を実装し、境界fixtureと途中失敗の再開テストを通してからAIやレポートへ渡します。Dataが処理中に変わる要件があり、snapshotを保証できない場合は、件数一致だけでcompleteとせず取得設計を見直してください。
Sources & scope
確認日:2026-10-15
Supabase JavaScript select: https://supabase.com/docs/reference/javascript/select(新しいタブで開く)
Supabase JavaScript v1 fetch/select(1,000 default、Project API Settings、range): https://supabase.com/docs/reference/javascript/v1/select(新しいタブで開く)
PostgREST Pagination and Count: https://postgrest.org/en/stable/references/api/pagination_count.html(新しいタブで開く)
Supabase range example: https://supabase.com/docs/reference/javascript/v1/select#limit-the-query-to-a-range(新しいタブで開く)
公式資料でSupabaseの既定最大行数・設定先・range()例、およびPostgRESTのrange header・Content-Range・count方式を確認しました。1,000行ちょうどは上限に一致する症状ですが、各projectの現在値、filter、RLS、同時更新状態は実際の環境で確認が必要です。上のコード、数値例、チェックリストは実装・実測結果ではなく検証用の設計例です。
