CDN & Storage

Upload files to global CDN storage with automatic optimization and fast delivery.

Upload Flow

File uploads use a two-step process for security and efficiency:

  1. Request a presigned upload URL from the API
  2. Upload the file directly to S3 using the presigned URL
  3. Confirm the upload is complete

Basic Upload

upload.ts
import { Stack0 } from '@stack0/sdk'
const stack0 = new Stack0({
apiKey: process.env.STACK0_API_KEY!
})
// Step 1: Get a presigned upload URL
const { uploadUrl, assetId, cdnUrl } = await stack0.cdn.getUploadUrl({
filename: 'profile.jpg',
mimeType: 'image/jpeg',
size: file.size, // File size in bytes
})
// Step 2: Upload directly to S3
await fetch(uploadUrl, {
method: 'PUT',
body: file,
headers: {
'Content-Type': 'image/jpeg',
},
})
// Step 3: Confirm the upload
await stack0.cdn.confirmUpload({ assetId })
// The file is now available at cdnUrl
console.log('File available at:', cdnUrl)

Upload Options

OptionTypeDescription
filenamestringOriginal filename (required)
mimeTypestringMIME type (required)
sizenumberFile size in bytes (required, max 100MB)
folderstringOrganize into folders
metadataobjectCustom metadata

Organize with Folders

folders.ts
// Upload to a specific folder
const { uploadUrl, assetId, cdnUrl } = await stack0.cdn.getUploadUrl({
filename: 'avatar.jpg',
mimeType: 'image/jpeg',
size: file.size,
folder: 'users/avatars',
})
// Upload product images
const productUpload = await stack0.cdn.getUploadUrl({
filename: 'hero.png',
mimeType: 'image/png',
size: file.size,
folder: 'products/SKU-12345',
})

Get Asset Details

get-asset.ts
const asset = await stack0.cdn.get({ id: 'asset_xxx' })
console.log('Filename:', asset.filename)
console.log('Size:', asset.size, 'bytes')
console.log('Type:', asset.type) // 'image' | 'video' | 'audio' | 'document' | 'other'
console.log('CDN URL:', asset.cdnUrl)
console.log('Dimensions:', asset.width, 'x', asset.height) // For images
console.log('Created:', asset.createdAt)

List Assets

list-assets.ts
// List all assets
const { assets, total, hasMore } = await stack0.cdn.list({
limit: 50,
offset: 0,
})
// Filter by folder
const userAvatars = await stack0.cdn.list({
folder: 'users/avatars',
})
// Filter by type
const images = await stack0.cdn.list({
type: 'image',
})
// Search by filename
const results = await stack0.cdn.list({
search: 'hero',
})
// Sort by date or size
const recent = await stack0.cdn.list({
sortBy: 'createdAt',
sortOrder: 'desc',
})

Update Asset

update-asset.ts
// Update asset metadata
const updated = await stack0.cdn.update({
id: 'asset_xxx',
filename: 'new-name.jpg',
folder: 'archived',
tags: ['product', 'hero'],
alt: 'Product hero image',
})

Move Assets

move-assets.ts
// Move multiple assets to a folder
const result = await stack0.cdn.move({
assetIds: ['asset_xxx', 'asset_yyy', 'asset_zzz'],
folder: 'archived/2024',
})
console.log(`Moved ${result.movedCount} assets`)
// Move to root folder
await stack0.cdn.move({
assetIds: ['asset_xxx'],
folder: null, // null = root
})

Delete Assets

delete-assets.ts
// Delete a single asset
await stack0.cdn.delete({ id: 'asset_xxx' })
// Delete multiple assets
const result = await stack0.cdn.deleteMany({
ids: ['asset_xxx', 'asset_yyy', 'asset_zzz'],
})
console.log(`Deleted ${result.deletedCount} assets`)

Browser Upload Example

Complete example for handling file uploads in a React component:

components/file-upload.tsx
// Client component
'use client'
import { useState } from 'react'
export function FileUpload() {
const [uploading, setUploading] = useState(false)
const [url, setUrl] = useState<string | null>(null)
async function handleUpload(e: React.ChangeEvent<HTMLInputElement>) {
const file = e.target.files?.[0]
if (!file) return
setUploading(true)
try {
// Get upload URL from your API
const res = await fetch('/api/upload', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
filename: file.name,
mimeType: file.type,
size: file.size,
}),
})
const { uploadUrl, assetId, cdnUrl } = await res.json()
// Upload to S3
await fetch(uploadUrl, {
method: 'PUT',
body: file,
headers: { 'Content-Type': file.type },
})
// Confirm upload
await fetch('/api/upload/confirm', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ assetId }),
})
setUrl(cdnUrl)
} finally {
setUploading(false)
}
}
return (
<div>
<input type="file" onChange={handleUpload} disabled={uploading} />
{uploading && <p>Uploading...</p>}
{url && <img src={url} alt="Uploaded" />}
</div>
)
}