Skip to content

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.

User uploads file → Server processes → Events sent via WebSocket → UI updates

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

BroadcasterBest ForNot Recommended For
MemoryDevelopment, single serverMulti-server production
RedisProduction, multi-server setupsServerless/edge
IORedisRedis clusters, enterpriseSimple 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)
import { memoryEventBroadcaster } from "@uploadista/event-broadcaster-memory";
// No configuration needed - just use it
const eventBroadcaster = memoryEventBroadcaster;

Events only work within a single server process. Perfect for development.

Connect to the WebSocket endpoint to receive real-time events:

// Connect to the upload's WebSocket
const 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 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"
}

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"
}
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>
);
}

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" });
}
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",
});
}
};

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
});

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)
);
  • 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
  • If using multiple servers, you must use Redis broadcaster (not Memory)
  • Check Redis connection is stable
  • Verify both publisher and subscriber connections are active
  • Add reconnection logic to your client
  • Check for proxy/load balancer timeouts (increase WebSocket timeout)
  • Ensure your server isn’t being restarted during uploads
  • 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