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 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.

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.mdchand-written for this tool
.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.