Supabase provides a storage solution through its bucket system. In this DevTip, we'll explore how to export files to Supabase storage buckets using Ruby, making it easy to manage and serve files in your applications.

Prerequisites

Before we begin, ensure you have:

  • Ruby installed (version 2.7 or higher)
  • A Supabase account
  • Basic familiarity with Ruby programming

Setting up your environment

First, create a new Ruby project and add the required dependencies:

mkdir supabase-storage-demo
cd supabase-storage-demo
bundle init

Add the Supabase Ruby client to your Gemfile:

source 'https://rubygems.org'

gem 'supabase'

Install the dependencies:

bundle install

Configuration

Store your Supabase credentials securely using environment variables:

client = Supabase.create_client(
  ENV['SUPABASE_URL'],
  ENV['SUPABASE_ANON_KEY']
)

Never commit your Supabase credentials directly in your code.

Connecting to Supabase

Create a new Ruby file and establish the connection to Supabase:

require 'supabase'

client = Supabase.create_client(
  'YOUR_SUPABASE_URL',
  'YOUR_SUPABASE_ANON_KEY'
)

Creating a storage bucket

Before uploading files, you need to create a storage bucket:

def create_bucket(supabase, bucket_name)
  begin
    supabase.storage.create_bucket(bucket_name, public: true)
    puts "Bucket '#{bucket_name}' created"
  rescue => e
    puts "Error creating bucket: #{e.message}"
  end
end

create_bucket(supabase, 'my-files')

Understanding bucket permissions

When working with Supabase storage, it's important to understand the permission model:

  1. Bucket Operations: Creating, deleting, and modifying buckets typically requires admin privileges. These operations should be performed during setup or through admin interfaces.

  2. File Operations: File uploads, downloads, and deletions are controlled by Row Level Security (RLS) policies. By default:

    • Public buckets allow anyone to read files
    • File modifications (upload/delete) require authentication
    • Custom policies can be set for more granular control

Here's how to set up a basic RLS policy for your bucket in the Supabase dashboard:

-- Allow authenticated users to upload files
CREATE POLICY "Allow authenticated uploads"
ON storage.objects
FOR INSERT
TO authenticated
WITH CHECK (bucket_id = 'my-files');

-- Allow public file downloads
CREATE POLICY "Allow public downloads"
ON storage.objects
FOR SELECT
TO public
USING (bucket_id = 'my-files');

Adjust these policies based on your application's security requirements.

Exporting files to Supabase

Export files to your Supabase bucket:

def export_file(client, bucket_name, file_path, destination_path)
  begin
    File.open(file_path, 'rb') do |file|
      response = client.storage
        .from(bucket_name)
        .upload(destination_path, file)

      puts "File uploaded"
      puts "Public URL: #{get_public_url(client, bucket_name, destination_path)}"
    end
  rescue Supabase::Error => e
    puts "Supabase error: #{e.message}"
  rescue Errno::ENOENT => e
    puts "File not found: #{e.message}"
  rescue StandardError => e
    puts "Unexpected error: #{e.message}"
  end
end

def get_public_url(supabase, bucket_name, file_path)
  supabase.storage.from(bucket_name).get_public_url(file_path)
end

Usage example:

# Export a single file
export_file(supabase, 'my-files', 'path/to/local/image.jpg', 'uploads/image.jpg')

# Export multiple files
files = Dir.glob('path/to/files/*')
files.each do |file|
  destination = "uploads/#{File.basename(file)}"
  export_file(supabase, 'my-files', file, destination)
end

Error handling and validation

Implement error handling and file validation:

def validate_and_export_file(supabase, bucket_name, file_path, destination_path)
  unless File.exist?(file_path)
    raise "File not found: #{file_path}"
  end

  file_size = File.size(file_path)
  max_size = 50 * 1024 * 1024 # 50MB limit

  if file_size > max_size
    raise "File exceeds maximum size of 50MB"
  end

  allowed_types = %w[.jpg .jpeg .png .pdf .doc .docx]
  unless allowed_types.include?(File.extname(file_path).downcase)
    raise "Invalid file type. Allowed types: #{allowed_types.join(', ')}"
  end

  export_file(supabase, bucket_name, file_path, destination_path)
end

Managing uploaded files

List and manage files in your bucket:

def list_files(supabase, bucket_name, path = '')
  response = supabase.storage.from(bucket_name).list(path)

  if response.error.nil?
    response.data.each do |file|
      puts "Name: #{file['name']}"
      puts "Size: #{file['metadata']['size']} bytes"
      puts "Last modified: #{file['metadata']['lastModified']}"
      puts "---"
    end
  else
    puts "Error listing files: #{response.error}"
  end
end

def delete_file(supabase, bucket_name, file_path)
  response = supabase.storage.from(bucket_name).remove([file_path])

  if response.error.nil?
    puts "File deleted"
  else
    puts "Error deleting file: #{response.error}"
  end
end

Use cases

  • Uploading user-generated content
  • Storing and serving application assets
  • Creating backup systems
  • Managing document libraries
  • Handling media files for content management systems

Best practices

  • Use unique file names to prevent overwrites:

    require 'securerandom'
    
    def generate_unique_filename(original_filename)
      extension = File.extname(original_filename)
      "#{SecureRandom.uuid}#{extension}"
    end
    
  • Implement proper error handling

  • Validate file types and sizes before upload

  • Use appropriate bucket permissions

  • Clean up temporary files after upload

  • Configure CORS if uploading from browsers:

    -- In Supabase Dashboard SQL Editor
    ALTER BUCKET "my-files"
    ENABLE CORS 'http://localhost:3000';
    
  • Use structured paths for better organization:

    # Example: uploads/2024/01/unique-filename.jpg
    def generate_storage_path(filename)
      date = Time.now
      "uploads/#{date.year}/#{date.month}/#{filename}"
    end
    

Conclusion

Supabase storage buckets provide a reliable solution for file management in Ruby applications. With the examples provided, you can implement a robust file export system that handles various file types and sizes.

For more advanced media processing needs, consider using Transloadit alongside Supabase:

  • Image Processing: Automatic image optimization, resizing, and format conversion
  • Video Handling: Transcoding, thumbnails, and adaptive streaming
  • Audio Processing: Format conversion, waveform generation, and audio normalization
  • Document Processing: PDF generation, OCR, and document previews
  • AI Processing: Face detection, object recognition, and content moderation

Check out Transloadit's Ruby SDK to supercharge your file processing pipeline while using Supabase for storage.