Agent skill

supabase-storage

Supabase Storage for file uploads, downloads, buckets, and signed URLs. Use when uploading files, managing storage buckets, generating signed URLs, or handling images.

Stars 163
Forks 31

Install this agent skill to your Project

npx add-skill https://github.com/majiayu000/claude-skill-registry/tree/main/skills/other/other/supabase-storage

SKILL.md

Supabase Storage Skill

File storage, uploads, downloads, and bucket management.

Quick Reference

Task Method
Upload file storage.from('bucket').upload(path, file)
Download file storage.from('bucket').download(path)
Get public URL storage.from('bucket').getPublicUrl(path)
Get signed URL storage.from('bucket').createSignedUrl(path, 3600)
Delete file storage.from('bucket').remove([path])
List files storage.from('bucket').list(folder)
Move file storage.from('bucket').move(from, to)
Copy file storage.from('bucket').copy(from, to)

Bucket Configuration

Public vs Private

  • Public: Files accessible via URL without authentication
  • Private: Requires authentication or signed URLs

Create Bucket (Dashboard)

  1. Go to Storage in Dashboard
  2. Click "New bucket"
  3. Set name and public/private
  4. Configure file size limits and allowed MIME types

Create Bucket (SQL)

sql
INSERT INTO storage.buckets (id, name, public, file_size_limit, allowed_mime_types)
VALUES (
  'avatars',
  'avatars',
  true,
  5242880,  -- 5MB
  ARRAY['image/jpeg', 'image/png', 'image/webp']
);

Create Bucket (config.toml)

toml
[storage.buckets.avatars]
public = true
file_size_limit = "5MiB"
allowed_mime_types = ["image/png", "image/jpeg", "image/webp"]

[storage.buckets.documents]
public = false
file_size_limit = "50MiB"
allowed_mime_types = ["application/pdf"]

Upload Files

Basic Upload

javascript
const { data, error } = await supabase.storage
  .from('avatars')
  .upload('user-123/avatar.png', file)

Upload with Options

javascript
const { data, error } = await supabase.storage
  .from('avatars')
  .upload('user-123/avatar.png', file, {
    cacheControl: '3600',
    contentType: 'image/png',
    upsert: true  // Replace if exists
  })

Upload from Browser

javascript
const fileInput = document.querySelector('input[type="file"]')
const file = fileInput.files[0]

const { data, error } = await supabase.storage
  .from('uploads')
  .upload(`${userId}/${file.name}`, file)

Upload Base64

javascript
const base64Data = 'data:image/png;base64,iVBOR...'
const base64 = base64Data.split(',')[1]
const buffer = Uint8Array.from(atob(base64), c => c.charCodeAt(0))

const { data, error } = await supabase.storage
  .from('images')
  .upload('photo.png', buffer, {
    contentType: 'image/png'
  })

Download Files

Download as Blob

javascript
const { data, error } = await supabase.storage
  .from('documents')
  .download('report.pdf')

// data is a Blob
const url = URL.createObjectURL(data)

Download to Browser

javascript
const { data } = await supabase.storage
  .from('documents')
  .download('report.pdf')

const link = document.createElement('a')
link.href = URL.createObjectURL(data)
link.download = 'report.pdf'
link.click()

Get URLs

Public URL (Public Buckets)

javascript
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user-123/avatar.png')

console.log(data.publicUrl)
// https://xxx.supabase.co/storage/v1/object/public/avatars/user-123/avatar.png

Signed URL (Private Buckets)

javascript
const { data, error } = await supabase.storage
  .from('documents')
  .createSignedUrl('private/report.pdf', 3600)  // 1 hour

console.log(data.signedUrl)

Multiple Signed URLs

javascript
const { data, error } = await supabase.storage
  .from('documents')
  .createSignedUrls(['doc1.pdf', 'doc2.pdf'], 3600)

Signed Upload URL

javascript
const { data, error } = await supabase.storage
  .from('uploads')
  .createSignedUploadUrl('user-123/file.pdf')

// data.signedUrl is valid for 2 hours
// data.token is the upload token

List Files

List All in Folder

javascript
const { data, error } = await supabase.storage
  .from('uploads')
  .list('user-123')

// data: [{ name, id, metadata, ... }]

With Options

javascript
const { data, error } = await supabase.storage
  .from('uploads')
  .list('user-123', {
    limit: 100,
    offset: 0,
    sortBy: { column: 'created_at', order: 'desc' }
  })

Search Files

javascript
const { data, error } = await supabase.storage
  .from('uploads')
  .list('user-123', {
    search: 'report'  // Filename contains 'report'
  })

Delete Files

Single File

javascript
const { data, error } = await supabase.storage
  .from('uploads')
  .remove(['user-123/old-file.pdf'])

Multiple Files

javascript
const { data, error } = await supabase.storage
  .from('uploads')
  .remove([
    'user-123/file1.pdf',
    'user-123/file2.pdf',
    'user-123/file3.pdf'
  ])

Move & Copy

Move File

javascript
const { data, error } = await supabase.storage
  .from('uploads')
  .move('old-path/file.pdf', 'new-path/file.pdf')

Copy File

javascript
const { data, error } = await supabase.storage
  .from('uploads')
  .copy('original/file.pdf', 'backup/file.pdf')

Image Transformations

Available on Pro plan and above.

Resize

javascript
const { data } = supabase.storage
  .from('avatars')
  .getPublicUrl('user-123/photo.jpg', {
    transform: {
      width: 200,
      height: 200,
      resize: 'cover'  // cover, contain, fill
    }
  })

Quality

javascript
const { data } = supabase.storage
  .from('images')
  .getPublicUrl('photo.jpg', {
    transform: {
      width: 800,
      quality: 75  // 20-100
    }
  })

Format Conversion

javascript
const { data } = supabase.storage
  .from('images')
  .getPublicUrl('photo.png', {
    transform: {
      format: 'webp'  // webp, jpeg, png
    }
  })

Storage RLS Policies

Enable RLS

sql
-- RLS is enabled by default on storage.objects

Common Policies

sql
-- Users can view their own files
CREATE POLICY "Users can view own files"
ON storage.objects FOR SELECT
TO authenticated
USING (bucket_id = 'uploads' AND auth.uid()::text = (storage.foldername(name))[1]);

-- Users can upload to their folder
CREATE POLICY "Users can upload own files"
ON storage.objects FOR INSERT
TO authenticated
WITH CHECK (bucket_id = 'uploads' AND auth.uid()::text = (storage.foldername(name))[1]);

-- Users can delete their files
CREATE POLICY "Users can delete own files"
ON storage.objects FOR DELETE
TO authenticated
USING (bucket_id = 'uploads' AND auth.uid()::text = (storage.foldername(name))[1]);

Public Bucket Policy

sql
-- Anyone can view public files
CREATE POLICY "Public read"
ON storage.objects FOR SELECT
TO public
USING (bucket_id = 'public-images');

Error Handling

javascript
const { data, error } = await supabase.storage
  .from('uploads')
  .upload('file.pdf', file)

if (error) {
  if (error.message === 'The resource already exists') {
    console.log('File already exists')
  } else if (error.message.includes('exceeded')) {
    console.log('File too large')
  } else if (error.message.includes('mime type')) {
    console.log('Invalid file type')
  } else {
    console.error('Upload error:', error.message)
  }
}

Size Limits

Plan Max File Size
Free 50 MB
Pro+ 500 GB

References

  • storage-policies.md - RLS policy patterns
  • upload-patterns.md - Upload best practices

Expand your agent's capabilities with these related and highly-rated skills.

Didn't find tool you were looking for?

Be as detailed as possible for better results