Skip to main content

Storage

Balda provides a unified storage interface for managing files across different storage providers (S3, Azure Blob, Local).

Quick Setup

Use the init-storage command to install dependencies and create configuration:

npx balda init-storage -t s3
npx balda init-storage -t azure
npx balda init-storage -t local

Flags:

  • -t, --type <provider>: Storage provider (s3, azure, local) - required
  • -o, --output <path>: Output directory (default: src/storage/)

Example with custom output:

npx balda init-storage -t s3 -o src/config/

What It Does

  1. Checks dependencies - Skips installation if already present
  2. Installs required packages:
    • S3: @aws-sdk/client-s3, @aws-sdk/s3-request-presigner, @aws-sdk/cloudfront-signer
    • Azure: @azure/storage-blob
    • Local: No dependencies
  3. Creates config file - Generates {type}.config.ts with templates

Generated Configuration

S3 Example

import { S3StorageProvider } from "balda";

export const s3Provider = new S3StorageProvider({
s3ClientConfig: {
bucketName: process.env.S3_BUCKET || "your-bucket-name",
region: process.env.AWS_REGION || "us-east-1",
credentials: {
accessKeyId: process.env.AWS_ACCESS_KEY_ID || "",
secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || "",
},
},
// Optional CloudFront signed URLs
cloudfrontOptions: {
domainName: process.env.CLOUDFRONT_DOMAIN || "",
keyPairId: process.env.CLOUDFRONT_KEY_PAIR_ID || "",
privateKey: process.env.CLOUDFRONT_PRIVATE_KEY || "",
},
});

Azure Example

import { AzureBlobStorageProvider } from "balda";

export const azureProvider = new AzureBlobStorageProvider({
containerName: process.env.AZURE_CONTAINER_NAME || "your-container-name",
connectionString: process.env.AZURE_STORAGE_CONNECTION_STRING || "",
storageAccountName: process.env.AZURE_STORAGE_ACCOUNT || "",
storageAccountKey: process.env.AZURE_STORAGE_KEY || "",
});

Local Example

import { LocalStorageProvider } from "balda";

export const localProvider = new LocalStorageProvider({
directory: process.env.LOCAL_STORAGE_DIR || "./storage",
});
warning

LocalStorageProvider does not support signed URLs (getDownloadUrl/getUploadUrl). Use S3 or Azure Blob storage for presigned URL functionality.

Usage

Basic Operations

import { Storage } from "balda";
import { s3Provider } from "./storage/s3.config";

const storage = new Storage({ s3: s3Provider }, { defaultProvider: "s3" });

// Upload file
await storage.putObject("uploads/file.pdf", fileBuffer);

// Get download URL (expires in 3600 seconds by default)
// Note: Not supported with LocalStorageProvider
const url = await storage.getDownloadUrl("uploads/file.pdf", 7200);

// List files
const files = await storage.listObjects("uploads/");

// Download file (raw bytes)
const fileData = await storage.getObject("uploads/file.pdf");

// Delete file
await storage.deleteObject("uploads/file.pdf");

Return Types

The getObject method supports different return types via a second parameter:

// Raw bytes (default) - returns Uint8Array
const rawData = await storage.getObject("file.pdf", "raw");
const rawData2 = await storage.getObject("file.pdf"); // same as "raw"

// Text string - returns string
const textContent = await storage.getObject("document.txt", "text");
console.log(textContent); // "Hello, World!"

// ReadableStream - for large files or streaming
const stream = await storage.getObject("video.mp4", "stream");
const reader = stream.getReader();
while (true) {
const { done, value } = await reader.read();
if (done) break;
// Process chunk
}

When to use each:

  • "raw" (default): Small to medium files, need binary data
  • "text": Text files, need string content directly
  • "stream": Large files, need to process data incrementally

### Multiple Providers

```ts
import { Storage } from "balda";
import { s3Provider } from "./storage/s3.config";
import { localProvider } from "./storage/local.config";

const storage = new Storage(
{
s3: s3Provider,
local: localProvider,
},
{ defaultProvider: "local" }
);

// Use default provider (local)
await storage.putObject("file.txt", buffer);

// Switch to S3
await storage.use("s3").putObject("file.txt", buffer);

Provider Capabilities

Different storage providers have varying levels of support for certain features:

FeatureLocalS3Azure Blob
putObject
getObject
listObjects
deleteObject
getDownloadUrl
getUploadUrl
tip

For production applications requiring presigned URLs for direct client uploads/downloads, use S3 or Azure Blob storage. Local storage is best suited for development or applications where files are accessed only through your server.

Interface

All storage providers implement these methods:

interface StorageInterface {
getDownloadUrl(key: string, expiresInSeconds?: number): Promise<string>;
getUploadUrl(key: string, expiresInSeconds?: number): Promise<string>;
listObjects(prefix?: string): Promise<string[]>;
getObject<T>(key: string, returnType?: "raw" | "text" | "stream"): Promise<T | undefined>;
putObject<T>(key: string, value: T, contentType?: string): Promise<void>;
deleteObject(key: string): Promise<void>;
}

Return type mapping:

  • "raw"Uint8Array (default)
  • "text"string
  • "stream"ReadableStream

Environment Variables

S3

AWS_ACCESS_KEY_ID=your-access-key
AWS_SECRET_ACCESS_KEY=your-secret-key
AWS_REGION=us-east-1
S3_BUCKET=your-bucket-name

# Optional for CloudFront
CLOUDFRONT_DOMAIN=d1234567890.cloudfront.net
CLOUDFRONT_KEY_PAIR_ID=your-key-pair-id
CLOUDFRONT_PRIVATE_KEY=your-private-key

Azure

AZURE_STORAGE_CONNECTION_STRING=your-connection-string
AZURE_CONTAINER_NAME=your-container-name
AZURE_STORAGE_ACCOUNT=your-account-name
AZURE_STORAGE_KEY=your-account-key

Local

LOCAL_STORAGE_DIR=./storage
info

Local storage does not require environment variables for signed URLs since this functionality is not supported. For presigned URLs, use S3 or Azure Blob storage providers.