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
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