Agent skill
supabase-storage
Manage file storage operations in Supabase Storage. Use for uploading, downloading, listing, and deleting files in buckets.
Install this agent skill to your Project
npx add-skill https://github.com/mhintz1980/ptl-lova/tree/main/claude-code-supabase-skills/skills/supabase-storage
SKILL.md
Supabase Storage Operations
Overview
This skill provides file storage operations through the Supabase Storage API. Supports bucket management, file uploads/downloads, listing files, generating URLs, and managing access control.
Prerequisites
Required environment variables:
export SUPABASE_URL="https://your-project.supabase.co"
export SUPABASE_KEY="your-anon-or-service-role-key"
Helper script: This skill uses the shared Supabase API helper. Make sure to source it:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
Bucket Operations
List Buckets
Get all storage buckets:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
supabase_get "/storage/v1/bucket"
Create Bucket
Create a new storage bucket:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
# Public bucket
supabase_post "/storage/v1/bucket" '{
"name": "avatars",
"public": true
}'
# Private bucket
supabase_post "/storage/v1/bucket" '{
"name": "private-documents",
"public": false,
"file_size_limit": 52428800,
"allowed_mime_types": ["image/png", "image/jpeg", "application/pdf"]
}'
Get Bucket Details
Retrieve bucket information:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
supabase_get "/storage/v1/bucket/avatars"
Update Bucket
Update bucket settings:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
curl -s -X PUT \
"${SUPABASE_URL}/storage/v1/bucket/avatars" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-H "Content-Type: application/json" \
-d '{
"public": false,
"file_size_limit": 10485760
}'
Delete Bucket
Delete a bucket (must be empty):
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
supabase_delete "/storage/v1/bucket/old-bucket"
Empty Bucket
Delete all files in a bucket:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
supabase_post "/storage/v1/bucket/avatars/empty" '{}'
File Operations
Upload File
Upload a file to storage:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
FILE_PATH="/path/to/local/image.jpg"
STORAGE_PATH="user-123/profile.jpg"
curl -s -X POST \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-F "file=@${FILE_PATH}"
Upload with upsert (overwrite if exists):
curl -s -X POST \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-H "x-upsert: true" \
-F "file=@${FILE_PATH}"
Upload with content type:
curl -s -X POST \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-H "Content-Type: image/jpeg" \
-F "file=@${FILE_PATH}"
Download File
Download a file from storage:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
STORAGE_PATH="user-123/profile.jpg"
OUTPUT_FILE="/path/to/save/downloaded.jpg"
curl -s -X GET \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-o "${OUTPUT_FILE}"
Download to stdout (pipe to other commands):
curl -s -X GET \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}"
List Files
List files in a bucket:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
supabase_post "/storage/v1/object/list/${BUCKET_NAME}" '{
"limit": 100,
"offset": 0,
"sortBy": {
"column": "name",
"order": "asc"
}
}'
List files in a specific folder:
supabase_post "/storage/v1/object/list/${BUCKET_NAME}" '{
"prefix": "user-123/",
"limit": 100
}'
Search files:
supabase_post "/storage/v1/object/list/${BUCKET_NAME}" '{
"search": "profile",
"limit": 50
}'
Delete File
Delete a single file:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
STORAGE_PATH="user-123/old-profile.jpg"
supabase_delete "/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}"
Delete multiple files:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
supabase_delete "/storage/v1/object/${BUCKET_NAME}" -d '{
"prefixes": ["user-123/temp/", "user-456/draft/"]
}'
Move/Rename File
Move or rename a file:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
FROM_PATH="user-123/temp.jpg"
TO_PATH="user-123/final.jpg"
supabase_post "/storage/v1/object/move" '{
"bucketId": "'"${BUCKET_NAME}"'",
"sourceKey": "'"${FROM_PATH}"'",
"destinationKey": "'"${TO_PATH}"'"
}'
Copy File
Copy a file to another location:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
SOURCE_PATH="user-123/profile.jpg"
DEST_PATH="user-123/profile-backup.jpg"
supabase_post "/storage/v1/object/copy" '{
"bucketId": "'"${BUCKET_NAME}"'",
"sourceKey": "'"${SOURCE_PATH}"'",
"destinationKey": "'"${DEST_PATH}"'"
}'
URL Generation
Get Public URL
Get public URL for a file (public buckets only):
BUCKET_NAME="avatars"
STORAGE_PATH="user-123/profile.jpg"
PUBLIC_URL="${SUPABASE_URL}/storage/v1/object/public/${BUCKET_NAME}/${STORAGE_PATH}"
echo "Public URL: $PUBLIC_URL"
Create Signed URL
Generate a signed URL for temporary access:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="private-documents"
STORAGE_PATH="user-123/secret.pdf"
EXPIRES_IN=3600 # 1 hour in seconds
supabase_post "/storage/v1/object/sign/${BUCKET_NAME}/${STORAGE_PATH}" '{
"expiresIn": '"${EXPIRES_IN}"'
}'
Response will include:
{
"signedURL": "/storage/v1/object/sign/private-documents/user-123/secret.pdf?token=..."
}
Full URL:
signed_path=$(supabase_post "/storage/v1/object/sign/${BUCKET_NAME}/${STORAGE_PATH}" '{"expiresIn": 3600}' | jq -r '.signedURL')
full_url="${SUPABASE_URL}${signed_path}"
echo "Signed URL: $full_url"
Create Multiple Signed URLs
Generate signed URLs for multiple files:
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="private-documents"
supabase_post "/storage/v1/object/sign/${BUCKET_NAME}" '{
"paths": ["doc1.pdf", "doc2.pdf", "folder/doc3.pdf"],
"expiresIn": 3600
}'
Common Patterns
Upload with Progress
#!/bin/bash
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="uploads"
FILE_PATH="/path/to/large-file.zip"
STORAGE_PATH="user-123/backup.zip"
echo "Uploading ${FILE_PATH}..."
curl -X POST \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-F "file=@${FILE_PATH}" \
--progress-bar -o /dev/null
if [[ $? -eq 0 ]]; then
echo "Upload successful!"
else
echo "Upload failed!"
exit 1
fi
Check if File Exists
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
STORAGE_PATH="user-123/profile.jpg"
# Try to get file info
response=$(curl -s -X GET \
"${SUPABASE_URL}/storage/v1/object/info/${BUCKET_NAME}/${STORAGE_PATH}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-w "\n%{http_code}")
http_code=$(echo "$response" | tail -n1)
if [[ $http_code -eq 200 ]]; then
echo "File exists"
else
echo "File does not exist"
fi
Batch Upload Files
#!/bin/bash
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="uploads"
LOCAL_DIR="/path/to/files"
for file in "$LOCAL_DIR"/*; do
filename=$(basename "$file")
echo "Uploading $filename..."
curl -s -X POST \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/batch/${filename}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-F "file=@${file}"
echo "✓ Uploaded $filename"
done
Download All Files from Bucket
#!/bin/bash
source "$(dirname "${BASH_SOURCE[0]}")/../../scripts/supabase-api.sh"
BUCKET_NAME="avatars"
OUTPUT_DIR="/path/to/download"
mkdir -p "$OUTPUT_DIR"
# Get file list
files=$(supabase_post "/storage/v1/object/list/${BUCKET_NAME}" '{"limit": 1000}')
# Download each file
echo "$files" | jq -r '.[].name' | while read -r filename; do
echo "Downloading $filename..."
curl -s -X GET \
"${SUPABASE_URL}/storage/v1/object/${BUCKET_NAME}/${filename}" \
-H "apikey: ${SUPABASE_KEY}" \
-H "Authorization: Bearer ${SUPABASE_KEY}" \
-o "${OUTPUT_DIR}/${filename}"
echo "✓ Downloaded $filename"
done
Image Transformations
Transform images on-the-fly (public buckets):
BUCKET_NAME="avatars"
STORAGE_PATH="user-123/profile.jpg"
# Resize to width 200px
TRANSFORMED_URL="${SUPABASE_URL}/storage/v1/render/image/public/${BUCKET_NAME}/${STORAGE_PATH}?width=200"
# Resize with quality
TRANSFORMED_URL="${SUPABASE_URL}/storage/v1/render/image/public/${BUCKET_NAME}/${STORAGE_PATH}?width=200&quality=80"
# Multiple transformations
TRANSFORMED_URL="${SUPABASE_URL}/storage/v1/render/image/public/${BUCKET_NAME}/${STORAGE_PATH}?width=300&height=300&resize=contain"
Access Control
Storage access is controlled by Storage Policies (RLS) in your Supabase dashboard.
Common policy examples:
Public read, authenticated write:
-- Set in Supabase Dashboard > Storage > Policies
-- Allow public to read
SELECT: true for all users
-- Allow authenticated users to upload their own files
INSERT: auth.uid() = (storage.foldername(name))[1]
User-specific folders:
-- Users can only access files in their own folder
SELECT: auth.uid() = (storage.foldername(name))[1]
UPDATE: auth.uid() = (storage.foldername(name))[1]
DELETE: auth.uid() = (storage.foldername(name))[1]
Error Handling
Common errors:
| Status | Error | Meaning |
|---|---|---|
| 400 | Invalid bucket name | Bucket name contains invalid characters |
| 404 | Object not found | File doesn't exist |
| 409 | Bucket already exists | Bucket with that name exists |
| 413 | Payload too large | File exceeds size limit |
| 422 | Invalid MIME type | File type not allowed in bucket |
Security Best Practices
- Use private buckets for sensitive data
- Implement Storage Policies (RLS) for access control
- Set file size limits to prevent abuse
- Restrict MIME types to expected file types
- Use signed URLs for temporary access to private files
- Organize files in user-specific folders (e.g.,
user-{uuid}/) - Never expose service role key to client applications
File Size Limits
- Default max file size: 50MB
- Can be configured per bucket up to 5GB (contact Supabase for larger)
- Check bucket settings for current limit
API Documentation
Full Supabase Storage API documentation: https://supabase.com/docs/guides/storage
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
vercel-react-best-practices
React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
web-design-guidelines
Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
nano-banana-pro-prompts-recommend-skill
Recommend suitable prompts from 6000+ Nano Banana Pro image generation prompts based on user needs. Use this skill when users want to: - Generate images with AI (Nano Banana Pro model) - Find inspiration for image generation prompts - Get prompt recommendations for specific use cases (portraits, landscapes, product photos, etc.) - Create illustrations for articles, videos, podcasts, or other content - Translate and understand prompt techniques
expo-react-native-typescript
Expert in Expo React Native TypeScript mobile development with best practices
expo-react-native-performance
Expo React Native performance optimization guidelines. This skill should be used when writing, reviewing, or refactoring Expo React Native code to ensure optimal performance patterns. Triggers on tasks involving React Native components, lists, animations, images, or performance improvements.
react-native-architecture
Build production React Native apps with Expo, navigation, native modules, offline sync, and cross-platform patterns. Use when developing mobile apps, implementing native integrations, or architecting React Native projects.
Didn't find tool you were looking for?