Skip to content

Flow Nodes Overview

Flow nodes are the building blocks of Uploadista processing pipelines. Each node performs a specific operation - from file routing to image optimization to virus scanning. Connect nodes together to create powerful file processing workflows.

CategoryPurposeNode PackagePlugin Packages
UtilityData flow control@uploadista/flow-utility-nodesflow-utility-zipjs
ImagesImage processing@uploadista/flow-images-nodesflow-images-sharp, flow-images-photon, flow-images-replicate
VideosVideo processing@uploadista/flow-videos-nodesflow-videos-av-node
DocumentsPDF and document processing@uploadista/flow-documents-nodesflow-documents-pdflib, flow-documents-unpdf, flow-documents-replicate
SecurityMalware scanning@uploadista/flow-security-nodesflow-security-clamscan

All transform nodes support these common options:

Controls how file data is processed for memory efficiency.

const node = yield* createResizeNode("resize", {
width: 800,
height: 600,
fit: "cover",
}, {
mode: "auto", // "auto" | "buffered" | "streaming"
streamingConfig: {
fileSizeThreshold: 1_048_576, // 1MB - switch to streaming above this
chunkSize: 65_536, // 64KB chunks
},
});
ModeDescriptionWhen to Use
autoAutomatically selects streaming for large filesDefault, recommended
bufferedLoads entire file into memorySmall files, predictable memory
streamingProcesses file as chunksLarge files, memory-constrained

Default Thresholds:

  • Images: 1MB (streaming for files > 1MB)
  • Videos: 10MB (streaming for files > 10MB)

Controls whether processed output is included in flow results.

const node = yield* createOptimizeNode("optimize", {
quality: 80,
format: "webp",
}, {
keepOutput: true, // Include output bytes in flow results
});
ValueDescription
false (default)Output is stored but not returned in results (saves memory)
trueOutput bytes included in flow results (useful for chaining or inspection)

Controls how output files are named after processing.

// Auto mode - appends node-specific suffix automatically
const autoNaming = yield* createResizeNode("resize", {
width: 800,
height: 600,
fit: "cover",
}, {
naming: { mode: "auto" }, // "photo.jpg" → "photo-800x600.jpg"
});
// Custom mode - use a template pattern
const customNaming = yield* createOptimizeNode("optimize", {
quality: 80,
format: "webp",
}, {
naming: {
mode: "custom",
pattern: "{{baseName}}-optimized.{{extension}}",
},
});
ModeDescriptionExample Output
autoAppends operation-specific suffixphoto-800x600.jpg, video-webm.webm
customUse template pattern with placeholdersphoto-optimized.webp

Template Placeholders:

  • {{baseName}} - Original filename without extension
  • {{extension}} - File extension
  • {{nodeId}} - Node identifier
  • {{nodeType}} - Type of operation (resize, optimize, etc.)
  • {{flowId}} - Flow identifier
  • {{jobId}} - Job identifier
NodeSpeedCostBackendStreaming
Resize (Sharp)50-100msFreeNode.jsYes
Resize (Photon)5-10msFreeEdgeNo
Optimize100-200msFreeSharp/PhotonYes
Remove BG5-15s$0.001AI/ReplicateNo
Describe Image10-20ms$0.001AI/ReplicateNo
Transcode Video1-60sFreeAV NodeYes
Resize Video1-60sFreeAV NodeYes
Trim Video1-30sFreeAV NodeYes
Describe Video100-500msFreeAV NodeNo
OCR5-15s$0.005AI/ReplicateNo
Extract TextLess than 1sFreeunpdfNo
Split PDFLess than 2sFreepdf-libNo
Merge PDFLess than 2sFreepdf-libNo
Scan Virus1-30sFreeClamAVNo
MergeInstantFreeMemoryNo
ConditionalInstantFreeMemoryNo
MultiplexInstantFreeMemoryNo
Zip100-200msFreeCPUNo

Multi-stage image processing with resize, optimize, and describe:

Input → Resize → Optimize → Describe Image
import { createFlow, createInputNode } from "@uploadista/core";
import {
createResizeNode,
createOptimizeNode,
createDescribeImageNode,
} from "@uploadista/flow-images-nodes";
const imagePipelineFlow = createFlow({
flowId: "image-pipeline-flow",
name: "Image Pipeline Flow",
nodes: {
input: createInputNode("input"),
resize: createResizeNode("resize", {
width: 1200,
height: 900,
fit: "cover",
}),
optimize: createOptimizeNode("optimize", {
quality: 85,
format: "webp",
}),
"describe-image": createDescribeImageNode("describe-image"),
},
edges: [
{ source: "input", target: "resize" },
{ source: "resize", target: "optimize" },
{ source: "optimize", target: "describe-image" },
],
});

Route files based on properties like size:

Input → Conditional (size > 2MB?)
├─ YES: Resize → Optimize Large
└─ NO: Optimize Small
import { createFlow, createInputNode } from "@uploadista/core";
import { createResizeNode, createOptimizeNode } from "@uploadista/flow-images-nodes";
import { createConditionalNode } from "@uploadista/flow-utility-nodes";
const conditionalImageFlow = createFlow({
flowId: "conditional-image-flow",
name: "Conditional Image Flow",
nodes: {
input: createInputNode("input"),
conditional: createConditionalNode("conditional", {
field: "size",
operator: "greaterThan",
value: 2000000, // 2MB
}),
resize: createResizeNode("resize", {
width: 1600,
height: 1200,
fit: "contain",
}),
"optimize-large": createOptimizeNode("optimize-large", {
quality: 80,
format: "webp",
}),
"optimize-small": createOptimizeNode("optimize-small", {
quality: 85,
format: "webp",
}),
},
edges: [
{ source: "input", target: "conditional" },
{ source: "conditional", target: "resize" },
{ source: "resize", target: "optimize-large" },
{ source: "conditional", target: "optimize-small" },
],
});

Generate multiple format versions and archive them:

Input → Multiplex (3 outputs)
├─ Optimize WebP
├─ Optimize JPEG
└─ Optimize PNG
→ Zip
import { createFlow, createInputNode } from "@uploadista/core";
import { createOptimizeNode } from "@uploadista/flow-images-nodes";
import { createMultiplexNode, createZipNode } from "@uploadista/flow-utility-nodes";
const multiFormatFlow = createFlow({
flowId: "multi-format-flow",
name: "Multi-Format Flow",
nodes: {
input: createInputNode("input"),
multiplex: createMultiplexNode("multiplex", {
outputCount: 3,
strategy: "copy",
}),
"optimize-webp": createOptimizeNode("optimize-webp", {
quality: 80,
format: "webp",
}),
"optimize-jpeg": createOptimizeNode("optimize-jpeg", {
quality: 85,
format: "jpeg",
}),
"optimize-png": createOptimizeNode("optimize-png", {
quality: 90,
format: "png",
}),
zip: createZipNode("zip", {
zipName: "multi-format.zip",
includeMetadata: false,
inputCount: 3,
}),
},
edges: [
{ source: "input", target: "multiplex" },
{ source: "multiplex", target: "optimize-webp" },
{ source: "multiplex", target: "optimize-jpeg" },
{ source: "multiplex", target: "optimize-png" },
{ source: "optimize-webp", target: "zip" },
{ source: "optimize-jpeg", target: "zip" },
{ source: "optimize-png", target: "zip" },
],
});

Scan files before processing:

Input → Scan Virus → Conditional (clean?)
├─ YES: Process → Store
└─ NO: Quarantine

Multi-stage video processing:

Input → Trim → Transcode → Thumbnail
import { createFlow, createInputNode } from "@uploadista/core";
import {
createTrimVideoNode,
createTranscodeVideoNode,
createVideoThumbnailNode,
} from "@uploadista/flow-videos-nodes";
const videoPipelineFlow = createFlow({
flowId: "video-pipeline-flow",
name: "Video Pipeline Flow",
nodes: {
input: createInputNode("input"),
trim: createTrimVideoNode("trim", {
startTime: 0,
endTime: 60,
}),
transcode: createTranscodeVideoNode("transcode", {
format: "webm",
codec: "vp9",
videoBitrate: "1000k",
}),
thumbnail: createVideoThumbnailNode("thumbnail", {
timestamp: 5,
format: "jpeg",
quality: 85,
}),
},
edges: [
{ source: "input", target: "trim" },
{ source: "trim", target: "transcode" },
{ source: "transcode", target: "thumbnail" },
],
});
  • Running on Node.js servers
  • Speed critical (sub-100ms)
  • Cost sensitive (free)
  • Simple operations
  • Self-hosted preference
  • Using Cloudflare Workers
  • Global users
  • Need instant response
  • Value simplicity
  • Need AI capabilities (background removal, upscaling)
  • Value accuracy over speed
  • Budget allows per-request cost
  • Advanced operations
  • Batch processing acceptable