select('*') without .limit() — unbounded result set
Part of the Performance advisors check · fix arrives as a pull request
What it is
A query uses select('*') with no .limit(), so it returns every column of every matching row.
Why it matters
The response grows with the table. Memory, egress, and response time all scale together, and the endpoint degrades steadily until it stops working rather than failing in a way anyone notices early.
What it looks like
// every column of every matching row, forever
const { data } = await supabase.from('orders').select('*');It works in the demo, it works at launch, and it keeps working right up until the table is big enough that it doesn’t.
Why nothing seems wrong for months
Supabase caps API responses with the project’s Max Rows setting (Dashboard → Settings → API), 1,000 by default. That cap is why the query never visibly explodes — and also why the bug is worse than it looks: past 1,000 rows your users are silently seeing an arbitrary subset, and every request is still paying to move 1,000 full rows — memory on the client, egress on the project — to render a screen that shows twenty.
Fix it manually
Name the columns the caller renders, cap the page, and paginate — offset pagination for admin screens, keyset for anything users scroll:
// offset pagination — fine for small admin lists
const { data } = await supabase
.from('orders')
.select('id, status, total, created_at')
.order('created_at', { ascending: false })
.range(0, 49); // rows 0–49, i.e. LIMIT 50 OFFSET 0
// keyset pagination — stable and index-friendly at any depth
const { data } = await supabase
.from('orders')
.select('id, status, total, created_at')
.lt('created_at', lastSeenCreatedAt)
.order('created_at', { ascending: false })
.limit(50);Keep Max Rows as the safety net, not the pagination strategy — lower it if nothing legitimately returns more than a few hundred rows.
How lumioguard fixes it
Each unbounded select is flagged with the table it reads and the columns the surrounding code actually uses. The fix arrives as a pull request replacing select('*') with that column list and adding an explicit limit, with a breakage analysis listing every call site the query feeds — you review and merge it.
Run them all on your app
Connect your repo and your live services with read-only scopes. The first scan is free, and nothing changes without your approval.