Fastify
Overview
Section titled “Overview”The Fastify adapter integrates Uploadista with Fastify applications, offering the highest performance for Node.js file upload servers. Features built-in WebSocket support and excellent TypeScript integration.
Installation
Section titled “Installation”npm install @uploadista/server @uploadista/adapters-fastify fastifypnpm add @uploadista/server @uploadista/adapters-fastify fastifyyarn add @uploadista/server @uploadista/adapters-fastify fastifyQuick Start
Section titled “Quick Start”First, define your flows:
import { createFlow, createInputNode, createOutputNode, createOptimizeNode,} from "@uploadista/flow";
export const optimizeFlow = createFlow({ flowId: "optimize-image", name: "Optimize Image", nodes: { input: createInputNode("input"), optimize: createOptimizeNode("optimize", { format: "webp", quality: 80 }), output: createOutputNode("output"), }, edges: [ { source: "input", target: "optimize" }, { source: "optimize", target: "output" }, ],});
export const flows = { "optimize-image": optimizeFlow,};Then set up the server:
import Fastify from "fastify";import { createUploadistaServer } from "@uploadista/server";import { fastifyAdapter } from "@uploadista/adapters-fastify";import { fileStore } from "@uploadista/data-store-filesystem";import { fileKvStore } from "@uploadista/kv-store-filesystem";import { flows } from "./flows";
const fastify = Fastify({ logger: true });
const uploadistaServer = await createUploadistaServer({ dataStore: fileStore({ directory: "./uploads" }), kvStore: fileKvStore({ directory: "./uploads" }), flows, adapter: fastifyAdapter(),});
// Mount routesfastify.all("/uploadista/api/*", async (request, reply) => { return uploadistaServer.handler({ request, reply });});
await fastify.listen({ port: 3000 });Configuration Options
Section titled “Configuration Options”import { imagePlugin } from "@uploadista/flow-images-sharp";import { redisEventBroadcaster } from "@uploadista/event-broadcaster-redis";
const uploadistaServer = await createUploadistaServer({ // Required dataStore: s3Store({ /* config */ }), // File storage backend kvStore: redisKvStore({ /* config */ }), // Metadata storage flows, // Flow definitions object adapter: fastifyAdapter({ authMiddleware: async (ctx) => ({ clientId: "user-123" }), // Optional }),
// Optional plugins: [imagePlugin()], // Processing plugins eventBroadcaster: redisEventBroadcaster({ /* config */ }), // Multi-instance sync withTracing: true, // OpenTelemetry tracing baseUrl: "uploadista", // API base path (default)});Complete Example
Section titled “Complete Example”import Fastify from "fastify";import websocket from "@fastify/websocket";import cors from "@fastify/cors";import rateLimit from "@fastify/rate-limit";import { createClient } from "redis";import { createUploadistaServer } from "@uploadista/server";import { fastifyAdapter } from "@uploadista/adapters-fastify";import { s3Store } from "@uploadista/data-store-s3";import { redisKvStore } from "@uploadista/kv-store-redis";import { imagePlugin } from "@uploadista/flow-images-sharp";import { flows } from "./flows";
// Initialize Redisconst redisClient = createClient({ url: process.env.REDIS_URL || "redis://localhost:6379",});await redisClient.connect();
const fastify = Fastify({ logger: true, bodyLimit: 50 * 1024 * 1024 });
await fastify.register(websocket);await fastify.register(cors, { origin: '*' });await fastify.register(rateLimit, { max: 100, timeWindow: '15 minutes' });
const uploadistaServer = await createUploadistaServer({ dataStore: s3Store({ deliveryUrl: process.env.CDN_URL!, s3ClientConfig: { bucket: process.env.S3_BUCKET!, region: process.env.AWS_REGION!, credentials: { accessKeyId: process.env.AWS_ACCESS_KEY_ID!, secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!, }, }, }), kvStore: redisKvStore({ redis: redisClient }), flows, plugins: [imagePlugin()], adapter: fastifyAdapter({ authMiddleware: async (ctx) => { const token = ctx.request.headers.authorization?.split(" ")[1]; if (!token) return null;
try { const payload = await verifyJWT(token, process.env.JWT_SECRET!); return { clientId: payload.sub, permissions: payload.permissions || [] }; } catch { return null; } }, }),});
// Binary upload supportfastify.addContentTypeParser( "application/octet-stream", { parseAs: "buffer" }, (req, body, done) => done(null, body));
// HTTP routesfastify.all("/uploadista/api/*", async (request, reply) => { return uploadistaServer.handler({ request, reply });});
// WebSocket routes for real-time progressfastify.get( "/uploadista/ws/upload/:uploadId", { websocket: true }, (socket, req) => uploadistaServer.websocketHandler(socket, req.raw));
fastify.get( "/uploadista/ws/flow/:jobId", { websocket: true }, (socket, req) => uploadistaServer.websocketHandler(socket, req.raw));
await fastify.listen({ port: 3000, host: '0.0.0.0' });
// Graceful shutdownprocess.on("SIGTERM", async () => { await uploadistaServer.dispose(); await redisClient.quit(); await fastify.close(); process.exit(0);});Performance Features
Section titled “Performance Features”- Built-in validation schemas
- Automatic request/response serialization
- Plugin ecosystem
- Fastest Node.js framework
- Built-in health endpoints
API Routes
Section titled “API Routes”The server automatically creates these routes:
POST /uploadista/api/upload Create uploadGET /uploadista/api/upload/:uploadId Get upload statusPATCH /uploadista/api/upload/:uploadId Upload chunkDELETE /uploadista/api/upload/:uploadId Delete upload
POST /uploadista/api/flow/:flowId/:storageId Execute flowGET /uploadista/api/jobs/:jobId/status Get job statusPATCH /uploadista/api/jobs/:jobId/resume/:nodeId Resume paused flow
GET /uploadista/api/health Health checkGET /uploadista/api/ready Readiness checkRelated Concepts
Section titled “Related Concepts”- Authentication - Authentication and permissions
- Uploadista Server
- Data Stores
- KV Stores
- Event System