Skip to main content
Sonamu provides an integrated storage system for file uploads and storage. It supports local file system (fs) and AWS S3, and you can easily switch between them based on the environment.

Basic Structure

import path from "path";
import { defineConfig } from "sonamu";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      default: process.env.DRIVE_DISK ?? "fs",
      drivers: {
        fs: drivers.fs({ /* ... */ }),
        s3: drivers.s3({ /* ... */ }),
      },
    },
  },
  // ...
});

default

Specifies the default storage driver to use. Type: string
export default defineConfig({
  server: {
    storage: {
      default: "fs",  // Use fs driver
      // ...
    },
  },
});
Switching with environment variables:
export default defineConfig({
  server: {
    storage: {
      default: process.env.DRIVE_DISK ?? "fs",  // Switch based on environment
      // ...
    },
  },
});
.env:
# Development: Local file system
DRIVE_DISK=fs

# Production: AWS S3
DRIVE_DISK=s3
Using environment variables allows you to switch storage without code changes.

drivers

Defines the storage drivers to use. Type: Record<string, Driver>
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      default: "fs",
      drivers: {
        fs: drivers.fs({ /* ... */ }),    // Local file system
        s3: drivers.s3({ /* ... */ }),    // AWS S3
      },
    },
  },
});

fs Driver

Stores files on the local file system. Suitable for development environments.
import path from "path";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      drivers: {
        fs: drivers.fs({
          location: path.join(import.meta.dirname, "/../public/uploaded"),
          visibility: "public",
          urlBuilder: {
            generateURL(key) {
              return `/api/public/uploaded/${key}`;
            },
            generateSignedURL(key) {
              return `/api/public/uploaded/${key}`;
            },
          },
        }),
      },
    },
  },
});

location

The directory path where files will be stored. Type: string (required)
drivers.fs({
  location: path.join(import.meta.dirname, "/../public/uploaded"),
  // ...
})
Path example:

visibility

Sets the access permissions for files. Type: "public" | "private"
  • "public": Anyone can access via URL
  • "private": Only authenticated users can access
drivers.fs({
  location: path.join(import.meta.dirname, "/../public/uploaded"),
  visibility: "public",  // Public files
  // ...
})

urlBuilder

Defines functions to generate file URLs.
drivers.fs({
  // ...
  urlBuilder: {
    // Generate regular URL
    generateURL(key) {
      return `/api/public/uploaded/${key}`;
    },

    // Generate signed URL (temporary access)
    generateSignedURL(key, expiresIn) {
      // In fs, usually same as regular URL
      return `/api/public/uploaded/${key}`;
    },
  },
})
generateURL: Generates the public URL for a file.
  • key: Unique key for the file (e.g., "profile-images/user-123.jpg")
  • Returns: Accessible URL
generateSignedURL: Generates a temporarily accessible signed URL.
  • key: Unique key for the file
  • expiresIn: Expiration time (seconds, optional)
  • Returns: Temporary URL

s3 Driver

Stores files on AWS S3. Suitable for production environments.
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      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",
          visibility: "private",
        }),
      },
    },
  },
});

credentials

Sets AWS authentication credentials. Type: (required)
credentials: {
  accessKeyId: string;
  secretAccessKey: string;
}
drivers.s3({
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
  },
  // ...
})
Always manage AWS credentials via environment variables. Never write them directly in code!
.env:
AWS_ACCESS_KEY_ID=your_access_key_here
AWS_SECRET_ACCESS_KEY=your_secret_key_here

region

The AWS region where the S3 bucket is located. Type: string (required)
drivers.s3({
  // ...
  region: "ap-northeast-2",  // Seoul region
})
Major regions:
  • ap-northeast-2 - Seoul
  • us-east-1 - N. Virginia
  • us-west-2 - Oregon
  • eu-west-1 - Ireland
  • ap-southeast-1 - Singapore

bucket

The S3 bucket name for storing files. Type: string (required)
drivers.s3({
  // ...
  bucket: "my-app-uploads",
})
Using different buckets per environment:
drivers.s3({
  // ...
  bucket: process.env.S3_BUCKET ?? "my-app-dev",
})

visibility

Sets the ACL (Access Control List) for S3 objects. Type: "public" | "private"
drivers.s3({
  // ...
  visibility: "private",  // Authentication required
})
  • "public": Public read allowed (public-read ACL)
  • "private": Private (private ACL, default)

Practical Examples

Development Environment: fs Only

import path from "path";
import { defineConfig } from "sonamu";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      default: "fs",
      drivers: {
        fs: drivers.fs({
          location: path.join(import.meta.dirname, "/../public/uploaded"),
          visibility: "public",
          urlBuilder: {
            generateURL(key) {
              return `/api/public/uploaded/${key}`;
            },
            generateSignedURL(key) {
              return `/api/public/uploaded/${key}`;
            },
          },
        }),
      },
    },
  },
  // ...
});

fs + S3 (Environment-based Switching)

import path from "path";
import { defineConfig } from "sonamu";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      default: process.env.DRIVE_DISK ?? "fs",  // Switch via environment variable

      drivers: {
        // Development: Local file system
        fs: drivers.fs({
          location: path.join(import.meta.dirname, "/../public/uploaded"),
          visibility: "public",
          urlBuilder: {
            generateURL(key) {
              return `/api/public/uploaded/${key}`;
            },
            generateSignedURL(key) {
              return `/api/public/uploaded/${key}`;
            },
          },
        }),

        // Production: AWS S3
        s3: drivers.s3({
          credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
          },
          region: process.env.S3_REGION ?? "ap-northeast-2",
          bucket: process.env.S3_BUCKET ?? "my-app-prod",
          visibility: "private",
        }),
      },
    },
  },
  // ...
});
.env.development:
DRIVE_DISK=fs
.env.production:
DRIVE_DISK=s3
S3_REGION=ap-northeast-2
S3_BUCKET=my-app-prod-uploads
AWS_ACCESS_KEY_ID=your_access_key_here
AWS_SECRET_ACCESS_KEY=your_secret_key_here

Multiple S3 Buckets

When using multiple S3 buckets for different purposes:
import { defineConfig } from "sonamu";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      default: "s3-public",

      drivers: {
        // For public files (profile images, etc.)
        "s3-public": drivers.s3({
          credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
          },
          region: "ap-northeast-2",
          bucket: "my-app-public",
          visibility: "public",
        }),

        // For private files (documents, receipts, etc.)
        "s3-private": drivers.s3({
          credentials: {
            accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
            secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
          },
          region: "ap-northeast-2",
          bucket: "my-app-private",
          visibility: "private",
        }),
      },
    },
  },
  // ...
});
Usage example:
import { StorageManager } from "sonamu/storage";

// Upload public image
await StorageManager.use("s3-public").put("profile.jpg", buffer);

// Upload private document
await StorageManager.use("s3-private").put("invoice.pdf", buffer);

File Upload Usage

After storage configuration, you can upload files in your API.
import { api } from "sonamu";
import { StorageManager } from "sonamu/storage";
import type { UploadedFile } from "sonamu";

export class FileModel {
  @api({ httpMethod: "POST" })
  static async upload(file: UploadedFile) {
    // Save file
    const key = `uploads/${Date.now()}-${file.filename}`;
    await StorageManager.put(key, file.buffer);

    // Generate URL
    const url = await StorageManager.url(key);

    return { key, url };
  }
}
File Upload Guide

S3 Bucket Setup

Before using S3, you need to create and configure a bucket in AWS Console.

1. Create Bucket

# Create bucket with AWS CLI
aws s3 mb s3://my-app-uploads --region ap-northeast-2

2. CORS Configuration

Configure CORS to allow direct uploads from frontend. S3 Console → Bucket → Permissions → CORS configuration:
[
  {
    "AllowedHeaders": ["*"],
    "AllowedMethods": ["GET", "PUT", "POST", "DELETE"],
    "AllowedOrigins": ["https://myapp.com"],
    "ExposeHeaders": ["ETag"]
  }
]

3. IAM Permissions

Sonamu needs appropriate IAM permissions to access S3. Minimum permissions policy:
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": [
        "s3:PutObject",
        "s3:GetObject",
        "s3:DeleteObject",
        "s3:ListBucket"
      ],
      "Resource": [
        "arn:aws:s3:::my-app-uploads",
        "arn:aws:s3:::my-app-uploads/*"
      ]
    }
  ]
}

Cautions

1. Environment Variable Security

// ❌ Bad: Credentials written directly in code
drivers.s3({
  credentials: {
    accessKeyId: "AKIAXXXXXXXXXXXXXXXX",
    secretAccessKey: "wJalrXUtnFEMI/K7MDENG/bPxRfiCY...",
  },
})

// ✅ Good: Use environment variables
drivers.s3({
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
  },
})

2. Choosing visibility

// Public files (profile images, product images, etc.)
visibility: "public"

// Private files (personal documents, receipts, contracts, etc.)
visibility: "private"

3. File Path Design

// ✅ Good: Systematic path structure
const key = `users/${userId}/profile/${Date.now()}.jpg`;
const key = `documents/${year}/${month}/${documentId}.pdf`;

// ❌ Bad: Flat structure
const key = `${Date.now()}.jpg`;

Next Steps

After completing storage configuration: