> ## Documentation Index
> Fetch the complete documentation index at: https://sonamu.cartanova.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Storage

> File storage driver settings

export const FileItem = ({name, description, children}) => {
  const isLeaf = !children;
  const ext = getFileExtension(name);
  const extStyle = ext ? getExtensionStyle(ext) : null;
  return <div style={{
    marginLeft: "30px"
  }}>
      <div style={{
    display: "flex",
    alignItems: "center",
    gap: "4px"
  }}>
        <span style={{
    position: "relative",
    display: "inline-block"
  }}>
          <span className="file-icon">{isLeaf && !name.endsWith("/") ? "📄" : "📁"}</span>
          {ext && <span style={{
    position: "absolute",
    bottom: "2px",
    right: "0px",
    fontSize: "5px",
    fontWeight: "bold",
    padding: "1px",
    borderRadius: "2px",
    lineHeight: "1",
    minWidth: "8px",
    textAlign: "center",
    ...extStyle
  }}>
              {ext}
            </span>}
        </span>
        <code>{name}</code>
        {description && <span className="description"> - {description}</span>}
      </div>
      {children}
    </div>;
};

export const FileTree = ({children}) => {
  return <div className="file-tree" style={{
    margin: "20px",
    marginLeft: "-30px"
  }}>
      {children}
    </div>;
};

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

```typescript theme={null}
import path from "path";
import { defineConfig } from "sonamu";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      drivers: {
        fs: drivers.fs({
          /* ... */
        }),
        s3: drivers.s3({
          /* ... */
        }),
      },
    },
  },
  // ...
});
```

## drivers

Defines the storage drivers to use. Two types are supported: `"fs"` (local file system) and `"s3"` (AWS S3).

**Type**: `Record<DriverKey, () => DriverContract>` (`DriverKey = "fs" | "s3"`)

```typescript theme={null}
import { drivers } from "sonamu/storage";

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

When saving files, you explicitly specify which driver to use in the `saveToDisk(diskName, key)` call. To use different drivers per environment, branch at the code level using environment variables.

### fs Driver

Stores files on the local file system. Suitable for development environments.

```typescript theme={null}
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)

```typescript theme={null}
drivers.fs({
  location: path.join(import.meta.dirname, "/../public/uploaded"),
  // ...
});
```

**Path example**:

<Card>
  <FileTree>
    <FileItem name="project-root/">
      <FileItem name="api/">
        <FileItem name="src/">
          <FileItem name="sonamu.config.ts" description="Runs here" />
        </FileItem>
      </FileItem>

      <FileItem name="public/">
        <FileItem name="uploaded/" description="File storage location">
          <FileItem name="profile-images/" />

          <FileItem name="documents/" />
        </FileItem>
      </FileItem>
    </FileItem>
  </FileTree>
</Card>

#### visibility

Sets the access permissions for files.

**Type**: `"public" | "private"`

* `"public"`: Anyone can access via URL
* `"private"`: Only authenticated users can access

```typescript theme={null}
drivers.fs({
  location: path.join(import.meta.dirname, "/../public/uploaded"),
  visibility: "public", // Public files
  // ...
});
```

#### urlBuilder

Defines functions to generate file URLs.

```typescript theme={null}
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.

```typescript theme={null}
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)

```typescript theme={null}
credentials: {
  accessKeyId: string;
  secretAccessKey: string;
}
```

```typescript theme={null}
drivers.s3({
  credentials: {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID ?? "",
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY ?? "",
  },
  // ...
});
```

<Warning>
  Always manage AWS credentials via environment variables. Never write them directly in code!
</Warning>

**.env**:

```bash theme={null}
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)

```typescript theme={null}
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)

```typescript theme={null}
drivers.s3({
  // ...
  bucket: "my-app-uploads",
});
```

**Using different buckets per environment**:

```typescript theme={null}
drivers.s3({
  // ...
  bucket: process.env.S3_BUCKET ?? "my-app-dev",
});
```

#### visibility

Sets the ACL (Access Control List) for S3 objects.

**Type**: `"public" | "private"`

```typescript theme={null}
drivers.s3({
  // ...
  visibility: "private", // Authentication required
});
```

* `"public"`: Public read allowed (`public-read` ACL)
* `"private"`: Private (`private` ACL, default)

## Practical Examples

### Development Environment: fs Only

```typescript theme={null}
import path from "path";
import { defineConfig } from "sonamu";
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}`;
            },
          },
        }),
      },
    },
  },
  // ...
});
```

### fs + S3 (Environment-based Switching)

Register both drivers, then select which driver to use in code based on an environment variable.

```typescript theme={null}
import path from "path";
import { defineConfig } from "sonamu";
import { drivers } from "sonamu/storage";

export default defineConfig({
  server: {
    storage: {
      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**:

```bash theme={null}
DRIVE_DISK=fs
```

**.env.production**:

```bash theme={null}
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
```

<Note>`DriverKey` currently supports only `"fs"` and `"s3"`. Multiple bucket configurations — such as separating public and private files into different buckets — are not supported at this time.</Note>

## File Upload Usage

After storage configuration, you can upload files using the `@upload` decorator and `BufferedFile.saveToDisk()`.

```typescript theme={null}
import { type DriverKey } from "sonamu/storage";
import { Sonamu } from "sonamu";
import { upload } from "sonamu/decorators";
import { BaseModelClass } from "sonamu";

export class FileModel extends BaseModelClass {
  @upload()
  static async upload() {
    const ctx = Sonamu.getContext();
    const file = ctx.bufferedFiles[0];
    if (!file) throw new Error("No uploaded file found.");

    // Select driver via environment variable
    const diskName: DriverKey = process.env.DRIVE_DISK === "s3" ? "s3" : "fs";
    const url = await file.saveToDisk(diskName, `uploads/${Date.now()}-${file.filename}`);

    return { url };
  }
}
```

→ [File Upload Guide](/en/api-development/file-upload/setup)

## S3 Bucket Setup

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

### 1. Create Bucket

```bash theme={null}
# 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**:

```json theme={null}
[
  {
    "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**:

```json theme={null}
{
  "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

```typescript theme={null}
// ❌ 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

```typescript theme={null}
// Public files (profile images, product images, etc.)
visibility: "public";

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

### 3. File Path Design

```typescript theme={null}
// ✅ 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:

* [file-upload](/en/api-development/file-upload/setup) - Implement file upload in API
* [cache](/en/configuration/sonamu-config/cache) - Cache settings
* [advanced-features/storage](/en/advanced-features/storage/storage-drivers) - Advanced storage features
