Overview
ThinkEx supports two storage backends for file uploads:- Supabase Storage (default) - Cloud-based object storage
- Local Filesystem - Server-side file storage
Storage Backends
Supabase Storage
Recommended for production deployments. Provides scalable, reliable cloud storage with CDN support.Configuration
.env
Set to
supabase to use Supabase Storage (default)Your Supabase project URL. Found in Project Settings > API > Project URL
Service role key with storage permissions. Found in Project Settings > API > service_role keyWarning: Keep this secret - never expose to client code
Bucket Setup
Create afile-upload bucket in your Supabase project:
- Go to Storage in Supabase dashboard
- Click New bucket
- Name:
file-upload - Public bucket: Enable (for public file access)
- File size limit: 200MB (or higher if needed)
- Allowed MIME types: Leave empty to allow all types
Bucket Policies
For authenticated-only uploads with public reads:SQL Policy
Upload Flow
- Client requests signed URL from
/api/upload-url - Server generates 5-minute signed upload URL using service role key
- Client uploads file directly to Supabase Storage
- File immediately accessible at public URL
Benefits
- Scalable: No server storage limits
- Fast: Global CDN distribution
- Reliable: Automatic backups and replication
- Secure: Signed URLs with expiration
- Cost-effective: Pay only for storage and bandwidth used
Limitations
- Requires Supabase project (free tier available)
- Network dependency for uploads and downloads
- Additional configuration complexity
Local Filesystem
Useful for development, testing, or when you need full control over file storage.Configuration
.env
Set to
local to use local filesystem storageDirectory for storing uploaded files (relative or absolute path)Created automatically if it doesn’t exist
Base URL of your application (used to generate file URLs)
Upload Flow
- Client requests upload URL from
/api/upload-url - Server responds with
mode: 'local' - Client falls back to
/api/upload-fileendpoint - Server saves file to
UPLOADS_DIR - Returns public URL:
{APP_URL}/api/files/{filename}
File Serving
You need to create an API route to serve uploaded files:src/app/api/files/[filename]/route.ts
Benefits
- Simple: No external dependencies
- Fast: Direct filesystem access
- Private: Full control over file access
- Free: No storage costs beyond server disk space
- Offline: Works without internet connectivity
Limitations
- Not scalable: Limited by server disk space
- No CDN: Files served from application server
- Deployment complexity: Files must be persisted across deployments
- No backup: You’re responsible for backups
- Single server: Not suitable for multi-server deployments
Storage Migration
To migrate between storage backends:Supabase to Local
Local to Supabase
File URL Structure
Supabase URLs
- Globally accessible via CDN
- No authentication required for public buckets
- Cached at edge locations
Local URLs
- Served by Next.js API route
- Requires
/api/files/[filename]route implementation - No CDN caching (unless behind reverse proxy)
Security Best Practices
Supabase Storage
- Never expose service role key: Only use in server-side code
- Use signed URLs: For temporary access to private files
- Set bucket policies: Restrict upload/delete to authenticated users
- Validate file types: Check MIME types before upload
- Scan for malware: Implement virus scanning if accepting user uploads
- Set size limits: Prevent abuse with reasonable file size caps
Local Storage
- Sanitize filenames: Remove path traversal characters (handled automatically)
- Validate paths: Never use user input directly in file paths
- Set permissions: Ensure uploads directory has correct permissions (chmod 755)
- Implement access control: Add authentication to file serving route if needed
- Monitor disk space: Prevent disk full errors
- Regular backups: Automate backup of uploads directory
Monitoring and Maintenance
Supabase Storage
Local Storage
Troubleshooting
Supabase Upload Fails
Error: “Failed to create upload URL”- Verify
NEXT_PUBLIC_SUPABASE_URLandSUPABASE_SERVICE_ROLE_KEY - Check bucket exists and is named
file-upload - Ensure service role key has storage permissions
- Check bucket policies allow uploads
- Verify signed URL hasn’t expired (5 minute limit)
- Ensure bucket is public or user is authenticated
Local Upload Fails
Error: “ENOENT: no such file or directory”- Check
UPLOADS_DIRpath exists or can be created - Verify write permissions on parent directory
- Use absolute path if relative path issues
- Fix directory permissions:
chmod 755 /path/to/uploads - Ensure process user has write access
Files Not Accessible
Supabase: Check bucket is public or RLS policies allow reads Local: Implement/api/files/[filename] route (see example above)
See Also
- File Upload API - Upload files to storage
- PDF Processing - Process uploaded PDFs