Supabase Storage
supabase-storageStorage35 lines
File and image storage with the same Row Level Security model as the database.
Current — as published in v1.3.2. Written by hand for Supabase Storage, not generated.
The rule
Buckets
- Private by default. A public bucket serves every object to anyone holding the URL, permanently and without an auth check. Make one public only when the content genuinely is.
- Set size limits and allowed MIME types on the bucket. A client-side check is a convenience, not a control.
Paths
- Namespace by owner:
{userId}/avatar.png. RLS policies are written against the path, so without a convention they cannot be expressed at all. - Decide the convention before the first upload; renaming later means moving every object.
Access
- Private objects are read through
createSignedUrlwith the shortest lifetime the use allows. Generate on demand — never store a signed URL, it expires. - Store the object path, not a URL.
Uploads
- Resize and compress before uploading. Camera originals are multi-megabyte and cost the user data and battery for pixels nobody sees.
- Always check the returned
error— the client returns it rather than throwing. - Set
contentTypeexplicitly.
Never
- Never put user content in a public bucket "for now".
- Never rely on an unguessable path as security — that is not access control.
- Never leave a user's objects behind when their account is deleted.
As each tool receives it
The same guidance, in the shape each editor reads. Cursor and Claude Code are written separately by hand; the rest are derived from the Cursor rule.
.cursor/rules/supabase-storage.mdc
---
description: Supabase Storage conventions
globs: ["src/services/storage/**", "src/services/supabase/**"]
alwaysApply: false
---
# Supabase Storage
## Buckets
- **Private by default.** A public bucket serves every object to anyone holding
the URL, permanently and without an auth check. Make one public only when the
content genuinely is.
- Set size limits and allowed MIME types on the bucket. A client-side check is a
convenience, not a control.
## Paths
- Namespace by owner: `{userId}/avatar.png`. RLS policies are written against
the path, so without a convention they cannot be expressed at all.
- Decide the convention before the first upload; renaming later means moving
every object.
## Access
- Private objects are read through `createSignedUrl` with the shortest lifetime
the use allows. Generate on demand — never store a signed URL, it expires.
- Store the object **path**, not a URL.
## Uploads
- Resize and compress before uploading. Camera originals are multi-megabyte and
cost the user data and battery for pixels nobody sees.
- Always check the returned `error` — the client returns it rather than throwing.
- Set `contentType` explicitly.
## Never
- Never put user content in a public bucket "for now".
- Never rely on an unguessable path as security — that is not access control.
- Never leave a user's objects behind when their account is deleted.
What else this module writes
Selecting Supabase Storage contributes more than a rule file — all of it merged with every other module you pick, with conflicts resolved rather than duplicated.
Environment
EXPO_PUBLIC_STORAGE_BUCKEToptionalDefault bucket for user uploads. Keep it private and namespace objects by user id.Dependencies
expo-image-picker^57.0.0expo-image-manipulator^57.0.0expo-file-system^57.0.0
Folders
src/services/storage/
Related
Put this in your repo
Adds Supabase Storage to a project this tool generated, without starting over. Files you have hand-edited are left alone.