Storage
Learn how to set up and use cloud storage for file uploads and media handling
MkSaaS uses Amazon S3 and compatible services like Cloudflare R2 for file storage and media handling, providing a reliable and scalable solution for storing images and other media files.
Setup
To set up storage in MkSaaS, follow these steps to configure the necessary environment variables:
Cloudflare R2 (Recommended)
- Create a Cloudflare account at cloudflare.com
- Create a new R2 bucket:
- Pick a globally unique bucket name (e.g.,
your-project-name
) - Select a region close to your target audience
- Set other options according to your needs
- Pick a globally unique bucket name (e.g.,
- Allow public access to the bucket:
- Settings/Public Development URL, click
Enable
- Set custom domains for public access to the bucket
- Save the public access URL as
STORAGE_PUBLIC_URL
- Settings/Public Development URL, click
- Create a new API Token:
- R2/API/Manage API Tokens, click
Create User API Token
- Set permissions to
Object Read & Write
to the bucket - Create the API Token, get the
Access Key ID
andSecret Access Key
- R2/API/Manage API Tokens, click
- Set the following environment variables:
STORAGE_REGION=your-region-or-auto
STORAGE_BUCKET_NAME=your-bucket-name
STORAGE_ACCESS_KEY_ID=your-access-key
STORAGE_SECRET_ACCESS_KEY=your-secret-key
STORAGE_ENDPOINT=https://xxx.r2.cloudflarestorage.com
STORAGE_PUBLIC_URL=https://pub-xxx.r2.dev
- Update the website config to use R2 as the storage provider:
export const websiteConfig = {
// ...other config
storage: {
provider: 's3',
},
// ...other config
}
Set storage provider to s3
because Cloudflare R2 is S3-compatible.
Amazon S3
-
Create an AWS account at aws.amazon.com
-
Create a new S3 bucket:
- Pick a globally unique bucket name (e.g.,
your-project-name
) - Select a region close to your target audience
- Disable "Block all public access" if you want files to be publicly accessible
- Set other options according to your needs
- Pick a globally unique bucket name (e.g.,
-
Add a bucket policy to allow public access. Go to the Permissions tab and add:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::your-bucket-name/*"
}
]
}
-
Create an IAM user with S3 access:
- Navigate to the IAM service
- Create a new policy with S3 permissions:
GetObject
,PutObject
,DeleteObject
,ListBucket
- Create a new IAM user with "Programmatic access"
- Attach the policy you created
- Save the Access Key ID and Secret Access Key
-
Add the following environment variables:
STORAGE_REGION=your-region
STORAGE_BUCKET_NAME=your-bucket-name
STORAGE_ACCESS_KEY_ID=your-access-key
STORAGE_SECRET_ACCESS_KEY=your-secret-key
- Update the website config to use S3 as the storage provider:
export const websiteConfig = {
// ...other config
storage: {
provider: 's3',
},
// ...other config
}
S3-Compatible Alternatives
MkSaaS is compatible with any S3-compatible storage service, including:
- DigitalOcean Spaces - Simple pricing and integrated with DO infrastructure
- Backblaze B2 - Very cost-effective option
- Google Cloud Storage - High performance with Google infrastructure
When using these alternatives, make sure to set the correct endpoint in your environment variables.
If you are setting up the environment, now you can go back to the Environment Setup guide and continue. The rest of this guide can be read later.
Environment Setup
Set up environment variables
Storage System Structure
The storage system in MkSaaS is designed with the following components:
This modular structure makes it easy to extend the storage system with new providers and functionality.
Core Features
- Direct file upload and management for server-side operations
- Browser-side file uploading with size-based optimization
- Pre-signed URLs for secure, direct-to-storage uploads
- Support for Amazon S3 and compatible storage services
- Folder organization for better file management
- Automatic path generation and file naming
- Configurable storage regions and bucket settings
- Support for public and private file storage
Usage
Basic File Operations
MkSaaS provides simple utilities for common file operations:
import { uploadFile, deleteFile } from '@/storage';
// Upload a file to storage
const { url, key } = await uploadFile(
fileBuffer,
'original-filename.jpg',
'image/jpeg',
'uploads/images'
);
// Delete a file from storage
await deleteFile(key);
Browser-Side Uploads
For uploading files directly from the browser, use the uploadFileFromBrowser
function:
'use client';
import { uploadFileFromBrowser } from '@/storage';
// In your React component
async function handleFileUpload(event) {
const file = event.target.files[0];
try {
// This will automatically use the most appropriate upload method
// based on the file size
const { url, key } = await uploadFileFromBrowser(file, 'uploads/images');
console.log('File uploaded:', url);
} catch (error) {
console.error('Upload failed:', error);
}
}
This function automatically chooses the best upload method:
- Small files (<10MB): Direct upload through your API endpoint
- Large files (>=10MB): Pre-signed URL for direct upload to S3
Using with Form Components
Here's an example of using the storage system with a form component:
'use client';
import { useState } from 'react';
import { uploadFileFromBrowser } from '@/storage/client';
import { Button } from '@/components/ui/button';
export function ImageUploader() {
const [isUploading, setIsUploading] = useState(false);
const [imageUrl, setImageUrl] = useState<string | null>(null);
async function handleFileChange(event: React.ChangeEvent<HTMLInputElement>) {
const file = event.target.files?.[0];
if (!file) return;
try {
setIsUploading(true);
const { url } = await uploadFileFromBrowser(file, 'profile-images');
setImageUrl(url);
} catch (error) {
console.error('Upload failed:', error);
} finally {
setIsUploading(false);
}
}
return (
<div className="space-y-4">
<div className="flex items-center gap-4">
<Button
variant="outline"
onClick={() => document.getElementById('file-upload')?.click()}
disabled={isUploading}
>
{isUploading ? 'Uploading...' : 'Upload Image'}
</Button>
<input
id="file-upload"
type="file"
accept="image/*"
className="hidden"
onChange={handleFileChange}
/>
</div>
{imageUrl && (
<div className="w-32 h-32 relative">
<img
src={imageUrl}
alt="Uploaded image"
className="object-cover w-full h-full rounded-md"
/>
</div>
)}
</div>
);
}
Customization
Creating a Custom Provider
MkSaaS makes it easy to extend the storage system with new providers:
-
Create a new file in the
src/storage/provider
directory -
Implement the
StorageProvider
interface
import {
PresignedUploadUrlParams,
StorageProvider,
UploadFileParams,
UploadFileResult
} from '@/storage/types';
export class CustomStorageProvider implements StorageProvider {
constructor() {
// Initialize your provider
}
public getProviderName(): string {
return 'CustomProvider';
}
async uploadFile(params: UploadFileParams): Promise<UploadFileResult> {
// Implementation for uploading a file
return { url: 'https://example.com/file.jpg', key: 'file.jpg' };
}
async deleteFile(key: string): Promise<void> {
// Implementation for deleting a file
}
}
- Update the provider selection in
index.ts
:
import { CustomStorageProvider } from './provider/custom-provider';
export const initializeStorageProvider = (): StorageProvider => {
if (!storageProvider) {
if (websiteConfig.storage.provider === 's3') {
storageProvider = new S3Provider();
} else if (websiteConfig.storage.provider === 'custom') {
storageProvider = new CustomStorageProvider();
} else {
throw new Error(
`Unsupported storage provider: ${websiteConfig.storage.provider}`
);
}
}
return storageProvider;
};
Best Practices
- File Organization: Use folders to organize files by type or purpose (e.g.,
uploads/images
,documents/contracts
) - File Size Limits: Set reasonable file size limits to prevent abuse
- File Type Validation: Validate file types on both client and server sides for security
- CDN Integration: Consider using a CDN in front of your storage for faster delivery
- Backup Strategy: Implement a backup strategy for important files
- Access Control: Use restrictive IAM policies for your storage user
- Error Handling: Properly handle and log storage errors
Next Steps
Now that you understand how to work with file storage in MkSaaS, explore these related topics: