Skip to content
Files SDK
Esc
↑↓navigate↵open⌘Jpreview
On this page

events

Normalize provider bucket notifications (S3, MinIO, R2, GCS, Azure, B2, Tigris, Supabase, Cloudinary, Appwrite, Box, Storj) into one FileEvent and route them to handlers by type and key glob.

The built-in events() plugin adds files.events: one place to react when a file is created or deleted, whether the write came through the SDK, a gateway upload, or straight to the bucket. The Events docs cover the concepts and per-provider setup; this page is the reference.

import { createFiles } from "files-sdk";
import { events } from "files-sdk/events";
import { r2 } from "files-sdk/r2";

const files = createFiles({
  adapter: r2({ bucket: "uploads" }),
  plugins: [events()],
});

files.events.on("created", "avatars/**", async (event) => {
  await resizeAvatar(event.key);
});

Options

events({
  format?: "s3" | "r2" | "gcs" | "azure" | "b2" | "tigris" | "supabase"
    | "cloudinary" | "appwrite" | "box" | "memory",
  bucket?: string,
  sdk?: boolean,
  dedupe?: { has(id): boolean | Promise<boolean>; add(id, ttl): void | Promise<void> },
  dedupeTtl?: number,
  onError?: (error: unknown, event: FileEvent) => void,
});
Option Default
format files.capabilities.events The notification format to read. Each adapter declares the one its provider sends: s3() and s3Fetch() against AWS (no endpoint, or one under amazonaws.com), minio, rustfs, wasabi and storj read "s3"; r2 reads "r2"; gcs and firebase-storage read "gcs"; azure reads "azure"; backblaze-b2 reads "b2"; tigris, supabase, cloudinary, appwrite, box and memory read their own. Every other adapter declares none. Set it for a provider that sends one of these formats under an adapter that doesn’t declare it (a non-AWS s3() endpoint, bun-s3).
bucket The adapter’s bucket Keep only provider events for this bucket (container, on Azure), so a queue, topic, or endpoint that also carries other buckets’ events (an EventBridge rule, an Azure system topic, a Supabase or Appwrite project webhook) delivers only yours. Defaults to the adapter’s own bucket when it exposes one, and filters only events that name a bucket. false accepts every bucket.
sdk false Also raise events for successful upload / delete / copy / move calls through this instance, with source: "sdk". Delivered after the call settles and not awaited. With sdk: true, list events() first in plugins: construction throws Invalid if a plugin before it maps keys or sizes (versioning, softDelete, dedup, encryption, compression, tiering, …), since the sdk source would report its internal keys and stored sizes.
dedupe None Skip event ids this store has seen. See Delivery.
dedupeTtl 24 hours How long a handled id is remembered, in ms.
onError console.error Called for every handler that throws, whatever the source. Unexpected webhook failures go to webhook({ onError }) instead.

files.events

on

on(type: "created" | "deleted" | "*", handler): () => void;
on(type: "created" | "deleted" | "*", pattern: string, handler): () => void;

Runs handler for events of type whose key matches pattern, a glob matched against the caller-facing key (** when omitted; the same glob syntax as search()). Handlers run one at a time, in registration order. Returns a function that removes the handler. When the instance has a notification format, on() runs the same startup check as webhook(), so a plugin that can’t map provider events throws here rather than on the first delivery.

dispatch

dispatch(input: unknown): Promise<FileEvent[]>;

Parses a delivery and runs the matching handlers. input can be a webhook Request, a message body (an SQS record body, a Queue message body, a Pub/Sub message), a whole consumer batch (a Lambda SQS event), a JSON string, or an array of any of those. Rejects when a handler throws, after every event has been tried, so a queue consumer can leave the message for redelivery. Resolves with the events it handled.

parse

parse(input: unknown): Promise<FileEvent[]>;

dispatch() without running handlers. Events outside the instance prefix or the bucket option are dropped, and installed plugins map the rest through their event hook. It doesn’t authenticate a Request; use webhook() for that.

webhook

webhook(opts: {
  verify:
    | false
    | { token: string }
    | { google: GoogleOidcOptions }
    | { secret: string; secondarySecret?: string; url?: string; now?: () => number }
    | { sns: SnsVerifyOptions };
  onError?: (cause: unknown, req: Request) => void;
}): { handle(req: Request): Promise<Response> };

An HTTP endpoint for providers that push, mountable with any gateway binding. onError receives failures the response doesn’t spell out (answered with a generic 500), and defaults to console.error; a handler’s error still goes to events({ onError }). See Webhooks for verification, handshakes and status codes.

emit

emit(events: FileEvent | FileEvent[]): Promise<void>;

Runs the matching handlers for events you already have, with no parsing or prefix mapping. Gateway uploads arrive this way. Rejects like dispatch().

settled

settled(): Promise<void>;

Resolves once every event delivered without an awaiting caller (memory adapter changes, sdk writes) has been handled. For tests.

format

The format this instance reads, or undefined when its adapter has none.

Composition

Plugins that store keys or bytes their callers never addressed map provider events through their event hook:

Plugin Provider events
dedup() Blob-store keys are dropped; a pointer’s size and ETag are cleared.
encryption(), compression() size is cleared: it’s the stored size.
versioning(), softDelete() Snapshot and trash keys are dropped, so a soft delete reads as one deleted.
tiering() Events for keys routed to the hot tier pass. With fallback: true, provider events throw: a hot-tier delete may be a move to cold. Use the gateway or sdk sources there.

Gateway and sdk events come from the instance’s own API and are already caller-facing, so they skip these hooks.

Last updated on

Was this page helpful?