Storage API

Complete guide to pushduck's storage API. Learn file operations, presigned URLs, storage instance management, and troubleshooting storage issues.

Storage API Overview

The Storage API provides direct access to file operations, metadata retrieval, and storage management. Use it for building custom file management interfaces, bulk operations, and advanced storage workflows.

API Categories

Getting Started

Basic Storage Setup

// lib/upload.ts
import { createUploadConfig } from 'pushduck/server'

const { storage } = createUploadConfig()
  .provider("cloudflareR2", {
    accessKeyId: process.env.AWS_ACCESS_KEY_ID,
    secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
    accountId: process.env.R2_ACCOUNT_ID,
    bucket: process.env.S3_BUCKET_NAME,
  })
  .build()

export { storage }

Using in API Routes

// app/api/files/route.ts
import { storage } from '@/lib/upload'

export async function GET() {
  try {
    const files = await storage.list.files()
    return Response.json({ files })
  } catch (error) {
    return Response.json({ error: 'Failed to list files' }, { status: 500 })
  }
}

export async function DELETE(request: Request) {
  const { key } = await request.json()
  
  try {
    await storage.delete.file(key)
    return Response.json({ success: true })
  } catch (error) {
    return Response.json({ error: 'Failed to delete file' }, { status: 500 })
  }
}

Common Operations

File Listing with Pagination

// Get paginated file list
const result = await storage.list.paginated({
  maxResults: 50,
  prefix: 'uploads/',
})

console.log(`Found ${result.files.length} files`)
if (result.hasMore) {
  console.log('More files available')
}

Bulk File Operations

// Delete multiple files
const filesToDelete = ['file1.jpg', 'file2.pdf', 'file3.png']
await storage.delete.files(filesToDelete)

// Get metadata for multiple files
const fileInfos = await Promise.all(
  fileKeys.map(key => storage.metadata.getInfo(key))
)

Secure Downloads

// Generate secure download URL
const downloadUrl = await storage.download.presignedUrl(
  'private/document.pdf',
  3600 // 1 hour expiration
)

// Use with fetch or window.open
window.open(downloadUrl)

Storage Instance Structure

The storage instance organizes operations into logical namespaces:

storage.list.*       // File listing operations
storage.metadata.*   // File metadata operations  
storage.download.*   // Download and URL operations
storage.upload.*     // Upload operations
storage.delete.*     // Delete operations
storage.validation.* // Validation operations

Error Handling

All storage operations use structured error handling:

import { isPushduckError } from 'pushduck/server'

try {
  const files = await storage.list.files()
} catch (error) {
  if (isPushduckError(error)) {
    console.log('Storage error:', error.code, error.context)
    
    switch (error.code) {
      case 'ACCESS_DENIED':
        // Handle permission errors
        break
      case 'NETWORK_ERROR':
        // Handle connectivity issues
        break
      default:
        // Handle other errors
    }
  }
}

TypeScript Support: All storage operations are fully typed with intelligent autocomplete and error detection.

Performance Considerations

Efficient File Listing

// ❌ Inefficient - loads all files
const allFiles = await storage.list.files()

// ✅ Efficient - use pagination
const result = await storage.list.paginated({ maxResults: 50 })

// ✅ Even better - filter with prefix
const userFiles = await storage.list.files({ 
  prefix: `users/${userId}/`,
  maxResults: 100 
})

Batch Operations

// ❌ Inefficient - multiple API calls
for (const key of fileKeys) {
  await storage.delete.file(key)
}

// ✅ Efficient - single batch operation
await storage.delete.files(fileKeys)

Next Steps