Event System
What is the Event System?
Section titled “What is the Event System?”The event system lets you receive real-time updates about uploads and file processing. When a user uploads a file, you can show them a live progress bar. When a flow finishes processing, you can notify them immediately.
Uploadista uses WebSockets to push events to your frontend, so users see updates in real-time without refreshing the page.
How It Works
Section titled “How It Works”User uploads file → Server processes → Events sent via WebSocket → UI updatesThe event system has two parts:
- Event Broadcasters - Share events between multiple server instances (for production)
- Event Emitters - Send events to connected WebSocket clients (for the browser)
For most setups, you just need to configure a broadcaster and Uploadista handles the rest.
When to Use Each Broadcaster
Section titled “When to Use Each Broadcaster”| Broadcaster | Best For | Not Recommended For |
|---|---|---|
| Memory | Development, single server | Multi-server production |
| Redis | Production, multi-server setups | Serverless/edge |
| IORedis | Redis clusters, enterprise | Simple deployments |
Quick Decision Guide:
- Development? Use Memory (no setup required)
- Single production server? Memory works, but Redis is better
- Multiple servers? Must use Redis (events need to reach all servers)
Quick Start
Section titled “Quick Start”Server Setup
Section titled “Server Setup”import { memoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
// No configuration needed - just use itconst eventBroadcaster = memoryEventBroadcaster;Events only work within a single server process. Perfect for development.
import { redisEventBroadcaster } from "@uploadista/event-broadcaster-redis";import { createClient } from "@redis/client";
// Redis pub/sub requires TWO connectionsconst redisPublisher = createClient({ url: process.env.REDIS_URL });const redisSubscriber = createClient({ url: process.env.REDIS_URL });
await redisPublisher.connect();await redisSubscriber.connect();
const eventBroadcaster = redisEventBroadcaster({ redis: redisPublisher, subscriberRedis: redisSubscriber,});Important: Redis pub/sub requires two separate connections - one for publishing events, one for subscribing.
Client Setup (Browser)
Section titled “Client Setup (Browser)”Connect to the WebSocket endpoint to receive real-time events:
// Connect to the upload's WebSocketconst ws = new WebSocket(`wss://api.example.com/uploadista/ws/upload/${uploadId}`);
ws.onmessage = (event) => { const message = JSON.parse(event.data);
switch (message.type) { case "upload_progress": // Update progress bar updateProgressBar(message.progress); console.log(`${Math.round(message.progress * 100)}% uploaded`); break;
case "upload_complete": // Show success message showSuccess("Upload complete!"); break;
case "upload_error": // Show error showError(message.error); break;
case "flow_progress": // Flow is processing console.log(`Processing: ${message.nodeId}`); break;
case "flow_complete": // Processing finished showSuccess("Processing complete!"); break; }};
ws.onerror = (error) => { console.error("WebSocket error:", error);};Events You’ll Receive
Section titled “Events You’ll Receive”Upload Events
Section titled “Upload Events”Events during file upload:
// Upload started{ "type": "upload_started", "uploadId": "abc123", "filename": "photo.jpg", "size": 5242880}
// Upload progress (sent frequently){ "type": "upload_progress", "uploadId": "abc123", "progress": 0.45, "bytesUploaded": 2359296, "totalBytes": 5242880}
// Upload complete{ "type": "upload_complete", "uploadId": "abc123", "url": "https://cdn.example.com/abc123/photo.jpg"}
// Upload failed{ "type": "upload_error", "uploadId": "abc123", "error": "Network connection lost"}Flow Events
Section titled “Flow Events”Events during file processing:
// Flow started{ "type": "flow_started", "jobId": "job-789", "uploadId": "abc123", "flowName": "Image Processing"}
// Node completed (for each processing step){ "type": "flow_progress", "jobId": "job-789", "nodeId": "resize", "nodeName": "Resize Image", "progress": 0.5}
// Flow complete{ "type": "flow_complete", "jobId": "job-789", "uploadId": "abc123", "outputs": { "thumbnail": "https://cdn.example.com/abc123/thumb.jpg", "fullsize": "https://cdn.example.com/abc123/full.jpg" }}
// Flow failed{ "type": "flow_error", "jobId": "job-789", "error": "Image format not supported"}Common Patterns
Section titled “Common Patterns”Progress Bar Component (React)
Section titled “Progress Bar Component (React)”import { useState, useEffect } from "react";
function UploadProgress({ uploadId }) { const [progress, setProgress] = useState(0); const [status, setStatus] = useState("uploading");
useEffect(() => { const ws = new WebSocket( `wss://api.example.com/uploadista/ws/upload/${uploadId}` );
ws.onmessage = (event) => { const message = JSON.parse(event.data);
if (message.type === "upload_progress") { setProgress(message.progress); } else if (message.type === "upload_complete") { setStatus("complete"); setProgress(1); } else if (message.type === "upload_error") { setStatus("error"); } };
return () => ws.close(); }, [uploadId]);
return ( <div className="progress-container"> <div className="progress-bar" style={{ width: `${progress * 100}%` }} /> <span>{Math.round(progress * 100)}%</span> {status === "complete" && <span>Complete!</span>} {status === "error" && <span>Upload failed</span>} </div> );}Multiple Upload Tracking
Section titled “Multiple Upload Tracking”Track several uploads at once:
const uploads = new Map();
function trackUpload(uploadId) { const ws = new WebSocket( `wss://api.example.com/uploadista/ws/upload/${uploadId}` );
ws.onmessage = (event) => { const message = JSON.parse(event.data);
// Update the upload state uploads.set(uploadId, { progress: message.progress || uploads.get(uploadId)?.progress || 0, status: message.type.replace("upload_", ""), });
// Trigger UI update renderUploadList(); };
uploads.set(uploadId, { progress: 0, status: "uploading" });}Notification Toast on Completion
Section titled “Notification Toast on Completion”ws.onmessage = (event) => { const message = JSON.parse(event.data);
if (message.type === "flow_complete") { // Show toast notification showToast({ title: "Processing Complete", message: `Your file has been processed successfully.`, type: "success", }); }
if (message.type === "flow_error") { showToast({ title: "Processing Failed", message: message.error, type: "error", }); }};Server Configuration
Section titled “Server Configuration”Using with Uploadista Server
Section titled “Using with Uploadista Server”Event broadcasters are passed during server setup:
import { createUploadistaServer } from "@uploadista/server";import { redisEventBroadcaster } from "@uploadista/event-broadcaster-redis";
const uploadista = await createUploadistaServer({ dataStore: s3Store({ /* ... */ }), kvStore: redisKvStore({ redis }), eventBroadcaster: redisEventBroadcaster({ redis: redisPublisher, subscriberRedis: redisSubscriber, }), // ... other options});WebSocket Endpoint Setup
Section titled “WebSocket Endpoint Setup”The Uploadista server exposes a WebSocket endpoint automatically. Mount it in your framework:
import { upgradeWebSocket } from "hono/cloudflare-workers";
app.get( "/uploadista/ws/upload/:uploadId", upgradeWebSocket(uploadista.websocketHandler));import expressWs from "express-ws";
const { app } = expressWs(express());
app.ws("/uploadista/ws/upload/:uploadId", uploadista.websocketHandler);import fastifyWebsocket from "@fastify/websocket";
await fastify.register(fastifyWebsocket);
fastify.get( "/uploadista/ws/upload/:uploadId", { websocket: true }, uploadista.websocketHandler);Troubleshooting
Section titled “Troubleshooting”Not receiving events
Section titled “Not receiving events”- Check the WebSocket connection is established (
ws.readyState === WebSocket.OPEN) - Verify the upload ID matches an active upload
- For multi-server setups, ensure Redis broadcaster is configured on all servers
Events delayed or missing in production
Section titled “Events delayed or missing in production”- If using multiple servers, you must use Redis broadcaster (not Memory)
- Check Redis connection is stable
- Verify both publisher and subscriber connections are active
WebSocket connection closing unexpectedly
Section titled “WebSocket connection closing unexpectedly”- Add reconnection logic to your client
- Check for proxy/load balancer timeouts (increase WebSocket timeout)
- Ensure your server isn’t being restarted during uploads
”Connection refused” on WebSocket
Section titled “”Connection refused” on WebSocket”- Verify the WebSocket endpoint is mounted correctly
- Check your URL uses
wss://(secure) in production - Ensure CORS is configured if connecting from a different domain
Related Concepts
Section titled “Related Concepts”- Upload Engine - Generates upload events
- Uploadista Server - Configure event broadcasting
- Flows Engine - Generates flow processing events
- KV Stores - Stores upload state (complementary to events)