import { StorageClient } from '@dcd-sdk/storage-client';
const storage = new StorageClient({
endpoint: process.env.DCD_STORAGE_ENDPOINT!,
apiKey: process.env.DCD_STORAGE_API_KEY!,
});
// Upload from disk path
const asset = await storage.upload('./banner.jpg', {
folder: 'marketing/2026',
filename: 'summer-sale.jpg',
access: 'public',
});
console.log('Uploaded asset:', asset.publicId, asset.secure_url);The client.upload() method handles file ingestion, multipart form encoding, network retries, and error handling for you.
Ingestion Sources Supported#
The SDK dynamically inspects the file input and streams it directly to your storage server:
1. Uploading from a Disk Path (Node.js & Python)
// Streams the file directly from disk without reading the entire file into RAM:
const asset = await storage.upload('./assets/product-hero.png', {
folder: 'products',
});2. Uploading from In-Memory Buffer or Bytes
Useful when receiving files from form submissions (e.g. Multer, Fastify Multipart) or dynamic image generation libraries:
import fs from 'node:fs';
const fileBuffer = fs.readFileSync('./report.pdf');
const asset = await storage.upload(fileBuffer, {
folder: 'reports',
filename: 'quarterly-report.pdf',
});Upload Options Reference#
Pass options as the second argument in TypeScript, or as keyword arguments in Python:
| Option | Type | Default | Description |
|---|---|---|---|
folder | string | Root "" | Target destination folder (e.g. 'users/avatars'). Nested paths are supported. |
filename | string | Auto | Custom filename to assign. If omitted, the original filename is preserved. |
publicId / public_id | string | Auto | Explicit unique asset identifier path without extension. |
access | 'public' | 'private' | 'public' | Access level. Private assets require HMAC signed URLs to download. |
overwrite | boolean | true | When true, replaces existing asset if publicId matches. |
uniqueFilename | boolean | false | Append random entropy suffix to guarantee unique naming. |
token | string | undefined | Short-lived signed upload token (enables client uploads with zero API key). |
tags | string[] | [] | Searchable metadata tags to attach to the asset. |
webhookUrl | string | undefined | Webhook URL notified asynchronously upon successful upload. |
onProgress | function | undefined | Browser progress callback: (percent: number) => void. |
timeoutMs | number | 30000 | Custom timeout for this upload request in milliseconds (Node.js). |
Returned Asset Schema#
When an upload completes, the server returns an AssetResult object with dual-format naming (both Cloudinary snake_case and TypeScript camelCase):
typescriptinterface AssetResult { // Core Cloudinary-compatible properties asset_id: string; // Unique internal asset UUID public_id: string; // e.g. "marketing/2026/summer-sale" version: number; // Epoch version timestamp version_id?: string; // Version hash string bytes: number; // File size in bytes format: string; // Detected format extension ("jpg", "png", "webp", "pdf") resource_type: string; // "image" | "raw" url: string; // Direct delivery URL secure_url: string; // HTTPS CDN URL folder: string; // Folder path created_at: string; // ISO 8601 timestamp // TypeScript camelCase properties id: string; publicId: string; resourceType: 'image' | 'raw'; mimeType: string; // Validated binary MIME type (e.g. "image/jpeg") width?: number; // Image width in pixels (images only) height?: number; // Image height in pixels (images only) access: 'public' | 'private'; storageDriver: 'local' | 's3'; secureUrl: string; createdAt: string; }
Binary Magic-Bytes Validation#
The storage server automatically inspects the binary magic-byte signature of every incoming file. If a file is renamed (e.g. a malicious .exe disguised as .jpg), the server rejects it with an UploadError and HTTP 415 status code before writing to disk.
Error Handling#
Wrap uploads in standard try/catch blocks:
import {
PayloadTooLargeError,
AuthenticationError,
StorageError
} from '@dcd-sdk/storage-client';
try {
const asset = await storage.upload('./giant-archive.zip');
} catch (err) {
if (err instanceof PayloadTooLargeError) {
console.error('File exceeds server MAX_FILE_SIZE_MB limit!');
} else if (err instanceof AuthenticationError) {
console.error('Invalid API Key provided.');
} else {
console.error('Upload failed:', err.message);
}
}Next Steps#
- URL Transformation Builder →: Generate resized, WebP-optimized delivery links.
- Direct Browser Uploads →: Offload uploads directly from client browsers to the storage cluster.
- Listing & Deleting Files →: Manage existing storage assets.