import { StorageClient } from '@dcd-sdk/storage-client';
const storage = new StorageClient({
endpoint: process.env.DCD_STORAGE_ENDPOINT!,
apiKey: process.env.DCD_STORAGE_API_KEY!,
});
// API route: POST /api/upload-token
export async function POST(req: Request) {
// Authenticate user session first...
const tokenData = await storage.createSignedUploadToken({
folder: 'user-uploads',
maxBytes: 10 * 1024 * 1024, // 10MB limit
expiresInSeconds: 300, // 5 minute TTL
access: 'public',
});
return Response.json(tokenData);
}When users upload large files (photos, PDFs, 1080p videos), sending those files through your backend application server causes memory spikes, blocked worker threads, and slow response times.
With Direct Browser Uploads, your backend only handles a tiny JSON token generation request (less than 1KB), while the heavy 50MB file streams directly to your DevCloudDesk Storage Server.
Step 1: Generate Signed Token on Your Backend#
Your backend server authenticates the user, then calls createSignedUploadToken():
import { StorageClient } from '@dcd-sdk/storage-client';
const storage = new StorageClient();
// In your Express or Next.js route:
app.post('/api/get-upload-token', async (req, res) => {
// 1. Verify user authentication
if (!req.user) return res.status(401).send('Unauthorized');
// 2. Generate short-lived token restricted to user's folder
const tokenPayload = await storage.createSignedUploadToken({
folder: `users/${req.user.id}/avatars`,
maxBytes: 5 * 1024 * 1024, // 5MB max
expiresInSeconds: 300, // 5 min TTL
access: 'public',
});
res.json(tokenPayload);
});Step 2: Upload Directly from Browser (React / Next.js / Vanilla JS)#
You can upload either using the official lightweight @dcd-sdk/storage-client package (recommended for live upload progress bar support) or using native browser fetch.
Option A: Using @dcd-sdk/storage-client (Recommended — Type-Safe with Progress)
Initialize createStorageClient with only your public storage server endpoint (no master API key is needed in browser code):
tsx'use client'; import React, { useState } from 'react'; import { createStorageClient } from '@dcd-sdk/storage-client'; // Client initialized with public storage endpoint only: const storage = createStorageClient({ endpoint: 'https://cdn.mycompany.com', }); export function AvatarUploader() { const [uploading, setUploading] = useState(false); const [progress, setProgress] = useState(0); const [avatarUrl, setAvatarUrl] = useState(''); const handleFileChange = async (e: React.ChangeEvent<HTMLInputElement>) => { const file = e.target.files?.[0]; if (!file) return; setUploading(true); setProgress(0); try { // 1. Get signed token from your backend API const tokenRes = await fetch('/api/get-upload-token', { method: 'POST' }); const { token } = await tokenRes.json(); // 2. Upload file directly with progress callback const asset = await storage.uploadWithToken(file, token, { onProgress: (percent) => setProgress(percent), }); console.log('Upload successful:', asset.publicId); setAvatarUrl(asset.secure_url); // 3. Save asset.publicId in your user database await fetch('/api/user/save-avatar', { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ publicId: asset.publicId }), }); } catch (err) { console.error('Direct upload failed:', err); } finally { setUploading(false); } }; return ( <div className="space-y-4"> <input type="file" accept="image/*" onChange={handleFileChange} disabled={uploading} /> {uploading && ( <div className="w-full bg-gray-200 rounded-full h-2.5"> <div className="bg-blue-600 h-2.5 rounded-full" style={{ width: `${progress}%` }} /> <p className="text-xs text-gray-500 mt-1">Uploading: {progress}%</p> </div> )} {avatarUrl && <img src={avatarUrl} alt="Uploaded Avatar" width={120} height={120} className="rounded-full" />} </div> ); }
Option B: Using Native Browser fetch (Zero Dependencies)
If you prefer not to bundle the client SDK in frontend assets, you can post directly to the storage server using standard browser FormData:
typescript// 1. Get signed token from your backend API const tokenRes = await fetch('/api/get-upload-token', { method: 'POST' }); const { token, uploadUrl } = await tokenRes.json(); // 2. Upload file directly to DevCloudDesk Storage Server const formData = new FormData(); formData.append('file', file); const uploadRes = await fetch(uploadUrl, { method: 'POST', headers: { 'X-Upload-Token': token, // Authenticates directly with Storage Server }, body: formData, }); const result = await uploadRes.json(); console.log('Upload successful:', result.asset.publicId); const avatarUrl = result.asset.secure_url;
Security Benefits#
- Zero Secret Leakage: Your master API key (
sk_live_...) never leaves your backend server. - Strict Time Limits: If the token is intercepted, it expires automatically after
expiresInSeconds. - Folder Sandboxing: The browser cannot upload to any folder other than the one specified by your backend.
- Hard Size Enforcement: If the client attempts to stream a file larger than
maxBytes, the storage server aborts the connection immediately.
Next Steps#
- Securing Private Files →: Protect confidential files with expiring download URLs.
- URL Transformation Builder →: Generate responsive avatars and thumbnails.