Supported Drivers
fs (File System)
Local file system
s3 (AWS S3)
AWS S3 compatible storage
fs Driver
Stores files in the local file system.Basic Configuration
import { drivers, type SonamuConfig } from "sonamu";
export const config: SonamuConfig = {
server: {
storage: {
default: 'fs',
drivers: {
fs: drivers.fs({
location: './uploads', // File storage location
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
}),
}
}
}
};
Configuration Options
- location
- urlBuilder
- visibility
File storage locationRecommended: Relative path from project root
fs: drivers.fs({
location: './uploads', // Relative path
})
fs: drivers.fs({
location: '/var/app/uploads', // Absolute path
})
URL generation rulesRequired: fs driver requires urlBuilder configurationExample:
fs: drivers.fs({
location: './uploads',
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
})
// key: 'avatars/user-1.png'
// URL: '/uploads/avatars/user-1.png'
File access permissions (Unix permissions)Default:
fs: drivers.fs({
location: './uploads',
visibility: 'public', // or 'private'
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
})
public (0644)public: Readable by all usersprivate: Readable only by owner (0600)
Practical Examples
Development Environment
export const config: SonamuConfig = {
server: {
storage: {
default: 'fs',
drivers: {
fs: drivers.fs({
location: './uploads',
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
}),
}
}
}
};
await disk.put('avatars/user-1.png', buffer);
// File location: ./uploads/avatars/user-1.png
// URL: /uploads/avatars/user-1.png
Static File Serving
You need to serve static files with Fastify:// sonamu.config.ts
import path from "path";
export const config: SonamuConfig = {
server: {
plugins: {
static: {
root: path.join(process.cwd(), 'uploads'),
prefix: '/uploads/',
}
},
storage: {
default: 'fs',
drivers: {
fs: drivers.fs({
location: './uploads',
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
}),
}
}
}
};
http://localhost:3000/uploads/avatars/user-1.png
→ Serves ./uploads/avatars/user-1.png file
s3 Driver
Stores files in AWS S3 or compatible storage.Basic Configuration
import { drivers, type SonamuConfig } from "sonamu";
export const config: SonamuConfig = {
server: {
storage: {
default: 's3',
drivers: {
s3: drivers.s3({
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: process.env.AWS_REGION!,
bucket: process.env.AWS_BUCKET!,
}),
}
}
}
};
Required Environment Variables
AWS_ACCESS_KEY_ID=AKIA...
AWS_SECRET_ACCESS_KEY=...
AWS_REGION=ap-northeast-2
AWS_BUCKET=my-app-uploads
Configuration Options
- credentials
- region
- bucket
- urlBuilder
AWS credentialsHow to obtain:
s3: drivers.s3({
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
// ...
})
- AWS Console → IAM
- Create user
- Grant S3 permissions (
AmazonS3FullAccess) - Create Access Key
AWS RegionMajor regions:
s3: drivers.s3({
region: 'ap-northeast-2', // Seoul
// ...
})
ap-northeast-2: Seoulap-northeast-1: Tokyous-east-1: N. Virginiaus-west-2: Oregon
S3 bucket nameCreating a bucket:
s3: drivers.s3({
bucket: 'my-app-uploads',
// ...
})
- AWS Console → S3
- Click “Create bucket”
- Enter a unique name
- Select region
- Configure public access blocking
URL generation rules (optional)Default: Uses S3’s default URLWhen using CloudFront:
s3: drivers.s3({
bucket: 'my-bucket',
urlBuilder: {
generateURL: (key) =>
`https://my-bucket.s3.amazonaws.com/${key}`,
}
})
https://bucket.s3.region.amazonaws.com/key
urlBuilder: {
generateURL: (key) =>
`https://d111111abcdef8.cloudfront.net/${key}`,
}
Practical Examples
Basic S3 Configuration
export const config: SonamuConfig = {
server: {
storage: {
default: 's3',
drivers: {
s3: drivers.s3({
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: 'ap-northeast-2',
bucket: 'my-app-uploads',
}),
}
}
}
};
await disk.put('avatars/user-1.png', buffer);
// S3 location: s3://my-app-uploads/avatars/user-1.png
// URL: https://my-app-uploads.s3.ap-northeast-2.amazonaws.com/avatars/user-1.png
CloudFront CDN Integration
export const config: SonamuConfig = {
server: {
storage: {
default: 's3',
drivers: {
s3: drivers.s3({
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: 'ap-northeast-2',
bucket: 'my-app-uploads',
urlBuilder: {
generateURL: (key) =>
`https://${process.env.CLOUDFRONT_DOMAIN}/${key}`,
}
}),
}
}
}
};
CLOUDFRONT_DOMAIN=d111111abcdef8.cloudfront.net
- Fast transfer speed (edge locations)
- Reduced bandwidth costs
- Caching
Public Bucket Configuration
To make an S3 bucket public:// S3 bucket policy (AWS Console → S3 → Bucket → Permissions → Bucket Policy)
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadGetObject",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-app-uploads/*"
}
]
}
S3 Compatible Storage
Cloudflare R2
export const config: SonamuConfig = {
server: {
storage: {
default: 'r2',
drivers: {
r2: drivers.s3({
credentials: {
accessKeyId: process.env.R2_ACCESS_KEY_ID!,
secretAccessKey: process.env.R2_SECRET_ACCESS_KEY!,
},
endpoint: `https://${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`,
region: 'auto',
bucket: process.env.R2_BUCKET!,
urlBuilder: {
generateURL: (key) =>
`https://${process.env.R2_PUBLIC_DOMAIN}/${key}`,
}
}),
}
}
}
};
- Free egress (no transfer costs)
- S3 API compatible
- Cloudflare network
MinIO (Self-hosted)
export const config: SonamuConfig = {
server: {
storage: {
default: 'minio',
drivers: {
minio: drivers.s3({
credentials: {
accessKeyId: process.env.MINIO_ACCESS_KEY!,
secretAccessKey: process.env.MINIO_SECRET_KEY!,
},
endpoint: 'http://localhost:9000',
region: 'us-east-1',
bucket: 'uploads',
forcePathStyle: true, // Required for MinIO
}),
}
}
}
};
Driver Comparison
| Feature | fs | S3 |
|---|---|---|
| Cost | Free (disk space) | Paid (storage + transfer) |
| Scalability | Limited (server disk) | Unlimited |
| Speed | Very fast | Fast (network) |
| Backup | Manual | Automatic (versioning) |
| CDN | Requires separate setup | CloudFront integration |
| Use case | Development, small scale | Production, large scale |
Practical Strategies
Development/Production Separation
const isDevelopment = process.env.NODE_ENV === 'development';
export const config: SonamuConfig = {
server: {
storage: {
default: isDevelopment ? 'fs' : 's3',
drivers: {
// Development: Local
fs: drivers.fs({
location: './uploads',
urlBuilder: {
generateURL: (key) => `/uploads/${key}`,
}
}),
// Production: S3
s3: drivers.s3({
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
},
region: process.env.AWS_REGION!,
bucket: process.env.AWS_BUCKET!,
}),
}
}
}
};
Multi-Bucket Strategy
export const config: SonamuConfig = {
server: {
storage: {
default: 'private',
drivers: {
// Private documents
private: drivers.s3({
credentials: { /* ... */ },
region: 'ap-northeast-2',
bucket: 'my-app-private',
}),
// Public files
public: drivers.s3({
credentials: { /* ... */ },
region: 'ap-northeast-2',
bucket: 'my-app-public',
}),
// Backup
backup: drivers.s3({
credentials: { /* ... */ },
region: 'ap-northeast-2',
bucket: 'my-app-backup',
}),
}
}
}
};
// Profile photos → public
await file.saveToDisk('avatars/user-1.png', 'public');
// Private documents → private (default)
await file.saveToDisk('documents/user-1/doc.pdf');
// Backup → backup
await Sonamu.storage.use('backup').put('backup.json', data);
Cautions
Cautions when configuring drivers:
-
Environment variable management: Never hardcode
// ❌ Hardcoded credentials: { accessKeyId: 'AKIA...', secretAccessKey: '...', } // ✅ Environment variables credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, } -
fs urlBuilder required: fs driver requires urlBuilder
// ❌ No urlBuilder fs: drivers.fs({ location: './uploads', }) // ✅ urlBuilder configured fs: drivers.fs({ location: './uploads', urlBuilder: { generateURL: (key) => `/uploads/${key}`, } }) -
S3 permissions check: Grant S3 permissions to IAM user
// Minimum permissions { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:PutObject", "s3:GetObject", "s3:DeleteObject" ], "Resource": "arn:aws:s3:::my-bucket/*" } ] } -
Bucket name uniqueness: S3 bucket names must be globally unique
// ❌ Name already in use bucket: 'uploads' // Error! // ✅ Unique name bucket: 'my-app-uploads-prod-2024' -
Region matching: bucket and region must match
// ❌ Region mismatch region: 'us-east-1', bucket: 'my-bucket' // Created in ap-northeast-2 // ✅ Region verified region: 'ap-northeast-2', bucket: 'my-bucket' // Created in ap-northeast-2
Next Steps
Storage Manager
Managing multiple disks
File Operations
File upload and download
URL Builder
Generating file URLs