Client API
Client-side APIs for file uploads including React hooks and property-based client access with built-in state management and progress tracking
Client API Overview
Pushduck provides two powerful client-side approaches for handling file uploads: useUploadRoute hook for reactive state management and Property-Based Client for enhanced type safety and modern developer experience.
All hooks follow React's rules of hooks - call them only from React function components or custom hooks, not from regular JavaScript functions.
Available APIs
createUploadClient (Property-Based)
Modern property-based client with enhanced type safety and developer experience
- Enhanced type safety with IntelliSense
- Property-based access (
client.imageUpload()
returns hook) - Built-in progress tracking and error handling
- Modern developer experience
Perfect for: New projects, enhanced type safety, modern React patterns
useUploadRoute
Route-specific uploads with enhanced validation and type safety
- Route-specific validation
- Enhanced type inference
- Multi-file support with progress tracking
- Advanced configuration and callbacks
- Reactive state management
Perfect for: All upload scenarios, the main React hook for uploads
Quick Comparison
Feature | createUploadClient | useUploadRoute |
---|---|---|
Approach | 🔧 Property-based | ⚛️ React Hook |
Type Safety | ✅ Excellent (IntelliSense) | ✅ Excellent |
Developer Experience | ✅ Modern | ✅ Advanced |
Simplicity | ✅ Intuitive | ✅ Straightforward |
Validation | ✅ Enhanced | ✅ Advanced |
Multi-route | ✅ Multiple routes | ✅ Single route per hook |
Progress Tracking | ✅ Built-in | ✅ Built-in |
Error Handling | ✅ Enhanced | ✅ Comprehensive |
Best For | 🆕 New projects, modern patterns | 🔧 All upload scenarios |
Basic Usage Examples
createUploadClient (Property-Based)
import { createUploadClient } from 'pushduck/client';
const client = createUploadClient<AppRouter>({
endpoint: '/api/upload',
});
function ModernUpload() {
// Each route returns a hook with all upload functionality
const {
uploadFiles,
files,
isUploading,
progress
} = client.imageUpload({
onSuccess: (results) => console.log('Upload successful!', results),
onError: (error) => console.error('Upload failed:', error),
});
const handleFileSelect = async (selectedFiles: File[]) => {
await uploadFiles(selectedFiles);
};
return (
<div>
<input
type="file"
multiple
onChange={(e) => e.target.files && handleFileSelect(Array.from(e.target.files))}
disabled={isUploading}
/>
{isUploading && <div>Progress: {progress}%</div>}
<div>Uploaded: {files.filter(f => f.status === 'success').length} files</div>
</div>
);
}
useUploadRoute Hook
import { useUploadRoute } from 'pushduck/client';
function AdvancedUpload() {
const {
uploadFiles,
files,
isUploading,
progress,
errors
} = useUploadRoute('imageUpload', {
endpoint: '/api/upload',
onSuccess: (results) => console.log('Upload successful!', results),
onError: (error) => console.error('Upload failed:', error),
});
const handleFileSelect = async (selectedFiles: File[]) => {
await uploadFiles(selectedFiles);
};
return (
<div>
<input
type="file"
multiple
onChange={(e) => e.target.files && handleFileSelect(Array.from(e.target.files))}
disabled={isUploading}
/>
{isUploading && <div>Progress: {progress}%</div>}
{errors.length > 0 && <div>Errors: {errors.join(', ')}</div>}
<div>Uploaded files: {files.filter(f => f.status === 'success').length}</div>
</div>
);
}
Hook Features
State Management
Both approaches provide reactive state management:
// useUploadRoute returns:
const {
uploadFiles, // Function to start upload
files, // Array of uploaded files with status
isUploading, // Boolean: upload in progress
progress, // Number: overall progress (0-100)
errors, // Array of error messages
reset, // Function to reset state
uploadSpeed, // Current upload speed (bytes/sec)
eta // Estimated time remaining (seconds)
} = useUploadRoute('routeName', config);
Progress Tracking
Real-time progress updates during file uploads:
// Progress updates automatically during upload
{isUploading && (
<div className="progress-bar">
<div
className="progress-fill"
style={{ width: `${progress}%` }}
/>
<span>{progress}% uploaded</span>
<span>Speed: {formatUploadSpeed(uploadSpeed)}</span>
<span>ETA: {formatETA(eta)}</span>
</div>
)}
Error Handling
Comprehensive error handling with detailed messages:
{errors.length > 0 && (
<div className="error-messages">
{errors.map((error, index) => (
<div key={index} className="error-message">
{error}
</div>
))}
</div>
)}
Configuration Options
Common Options
Both approaches support these configuration options:
const config = {
endpoint: '/api/upload', // Upload endpoint (default: '/api/s3-upload')
onStart: (files) => {}, // Called when upload starts
onProgress: (progress) => {}, // Progress callback (0-100)
onSuccess: (results) => {}, // Success callback with file results
onError: (error) => {}, // Error callback
};
useUploadRoute Specific Options
const routeConfig = {
// All common options plus:
endpoint: '/api/upload', // Route-specific endpoint
metadata: { userId: '123' }, // Upload metadata
};
Best Practices
Performance: Use createUploadClient
for multiple upload types in the same component. Use useUploadRoute
directly for single-purpose upload components.
File Validation: Always validate files on both client and server. Client validation provides immediate feedback, server validation ensures security.
Error Recovery: Both approaches include built-in retry logic for network errors and provide clear error messages for validation failures.
Next Steps
- Property-based client? → Start with createUploadClient
- Direct hook usage? → Use useUploadRoute
- Complete examples? → Check examples documentation
- Server setup? → See server configuration
Storage Troubleshooting
Solve storage API issues including access denied errors, empty file lists, presigned URL problems, and timeout issues. Complete debugging guide with error handling patterns.
createUploadClient
Create a type-safe upload client with property-based access and optional per-route configuration