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

Events overview

React when a file is created or deleted, however it got there. files-sdk/events turns each provider's bucket notifications into one event shape and routes them to your handlers.

files.upload() tells you about uploads made through the SDK. Plenty of writes aren’t: a file dropped in the console, another service writing to the bucket, a lifecycle rule expiring old objects, a browser that uploaded straight to storage and closed the tab before telling your server.

Every major provider can notify you about those writes, but each one does it differently: S3 sends to SQS, SNS, EventBridge or Lambda, MinIO to a webhook, R2 to a Queue, GCS to Pub/Sub, and Azure to Event Grid. files-sdk/events reads all of them and gives you one event:

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

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

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

files.events.on("deleted", async (event) => {
  await db.files.delete({ key: event.key });
});

Then point the provider’s notifications at it. For S3 delivering to SQS, the consumer is one line:

import { files } from "./lib/files";

export const handler = (event: unknown) => files.events.dispatch(event);

The event

interface FileEvent {
  type: "created" | "deleted";
  key: string; // caller-facing: URL-decoded, the instance prefix stripped
  size?: number;
  etag?: string;
  versionId?: string; // S3 version id, GCS generation, B2 / Box / Cloudinary version
  contentType?: string; // only when the delivery carries it (GCS, Azure, MinIO, Supabase, Cloudinary, Appwrite)
  time: number; // ms since the epoch
  source: "provider" | "gateway" | "sdk";
  provider: string; // the adapter's name
  id: string; // the idempotency key, see Delivery
  raw: unknown; // the provider's original record, untouched
}

There are only two types because that’s what providers can honestly tell you. Most can’t tell an overwrite from a new object, so both are created. A copy is a created and a move is a deleted plus a created. Tagging, metadata, restore and storage-class changes don’t create or remove anything, so they’re skipped.

Where events come from

source What it is How it arrives
"provider" The bucket’s own notification. It sees every write, from anywhere. dispatch() from a queue consumer, or webhook() for providers that push over HTTP. The memory adapter emits them natively.
"gateway" A browser upload through the gateway, once it completes. Automatic when the plugin is installed on the gateway’s files.
"sdk" A successful upload / delete / copy / move made through this instance. Opt in with events({ sdk: true }). It duplicates provider events for the same write, so it’s off by default. Use it where the provider has no notifications.

All three reach the same on() handlers.

Supported providers

Format Adapters Delivery Setup
s3 s3, s3-fetch (against AWS) Lambda, SQS, SNS → SQS, SNS → Lambda, EventBridge Amazon S3
s3 s3 SNS over HTTPS (signature-verified) Amazon S3
s3 minio Webhook MinIO
s3 rustfs Webhook (or Kafka, NATS, …) MinIO
s3 wasabi Your own AWS SNS topic Amazon S3
s3 storj Your own Google Pub/Sub topic (pull or push) Amazon S3
r2 r2 Queue (Worker consumer or HTTP pull) Cloudflare R2
gcs gcs, firebase-storage Pub/Sub (pull or push), Eventarc Google Cloud Storage
azure azure Event Grid webhook (either schema) Azure Blob Storage
b2 backblaze-b2 Signed webhook Backblaze B2
tigris tigris Webhook Tigris
supabase supabase Database Webhook on storage.objects Supabase
cloudinary cloudinary Signed webhook Cloudinary
appwrite appwrite Signed webhook Appwrite
box box Signed webhook (v2) Box
memory memory Built in Testing locally

Of the other S3-compatible services, Oracle, IBM COS, Alibaba, Tencent and Yandex use formats of their own; and DigitalOcean Spaces, Hetzner, Scaleway, Vultr, Exoscale, OVHcloud, Filebase, Akamai, Archil and Neon have no bucket notifications. An adapter whose provider isn’t verified claims no format; pass events({ format: "s3" }) to opt in when you know it sends S3’s. Calling parse() or dispatch() on an adapter with no format throws instead of guessing.

Dropbox, Google Drive, OneDrive and SharePoint webhooks only say “something changed” and expect you to call a changes API with a stored cursor, so they aren’t supported yet. Vercel Blob, Netlify Blobs, UploadThing, Convex, PocketBase, the filesystem, FTP, SFTP, WebDAV and Bunny have no notifications to read; use events({ sdk: true }) and the gateway there.

Next

Last updated on

Was this page helpful?