Skip to content
Files SDK
Esc
navigateopen⌘Jpreview
On this page

onAction

Runs once when a public call settles, on success and on failure - the constructor hook for audit logs, activity feeds, and per-action metrics.

A constructor hook that runs once when a public call settles, on success and on failure - status says which. Single-key operations report key; the array form reports the caller’s keys and emits one event for the whole call, carrying the aggregated result (any per-item failures live in that result’s errors). copy and move report from / to. Reach for it for audit logs, activity feeds, and per-action metrics.

const files = new Files({
  adapter: s3({ bucket: "uploads" }),
  hooks: {
    onAction(event) {
      logger.info("files", {
        action: event.type,
        status: event.status,
        target: event.keys ?? event.from ?? event.key,
        ms: event.durationMs,
      });
    },
  },
});

Identifying the call

type is the method name - "upload", "download", "copy", and so on. Which key field is set depends on the shape of that call:

  • Single-key calls (upload(key, …), download(key), delete(key), …) set key.
  • Array calls (upload([…]), delete([…]), …) set keys and fire one event for the whole batch - the per-item failures live in result.errors, not here.
  • copy and move set from / to instead of key.

On success, result is the call’s resolved value (an UploadResult, a StoredFile, a ListResult, …); on failure, error is the same FilesError delivered to onError and then thrown. Keys are always the ones you passed - the client prefix is never leaked.

import type { UploadResult } from "files-sdk";

hooks: {
  onAction(event) {
    if (event.type === "upload" && event.status === "success" && event.key) {
      const { size } = event.result as UploadResult;
      activity.push({ kind: "upload", key: event.key, size });
    }
  },
},

Per-action metrics

Because it fires for every method on success and failure with a wall-clock durationMs, one onAction covers latency and throughput across the whole surface - no per-call wrapping. It fires once per call, so a bulk upload([…]) of fifty objects emits a single timing for the batch, not fifty.

hooks: {
  onAction({ type, status, durationMs }) {
    metrics.timing(`files.${type}.duration`, durationMs, { status });
    metrics.increment(`files.${type}.${status}`);
  },
},

Provenance receipts

With the receipts option on, a successful mutating call (upload, delete, copy, move) also carries a receipt - a provenance record with the op, provider, key, byte size, etag, timing, and (when asked for) a SHA-256 of the upload body. It’s an additive field: absent on reads, on failures, and whenever receipts are off.

hooks: {
  onAction(event) {
    if (event.receipt) {
      provenance.record(event.receipt);
    }
  },
},

Like every hook, onAction is fire-and-forget: the SDK calls it but never awaits it, and a hook that throws can’t fail the operation it observes.

PropType
typeFilesActionType
TypeFilesActionType
key?string

Caller-facing key, for single-key operations.

Typestring
keys?string[]

Caller-facing keys, for the array form.

Typestring[]
from?string

`copy` source / destination, as passed by the caller.

Typestring
to?string
Typestring
status"success" | "error"
Type"success" | "error"
result?unknown

The resolved value, on success.

Typeunknown
error?FilesError

The error, on failure — also delivered to {@link FilesHooks.onError}.

TypeFilesError
durationMsnumber

Wall-clock duration of the public call, in milliseconds.

Typenumber
receipt?Receipt

A provenance {@link Receipt} for the call, present **only** when the `receipts` option is on and `type` is a mutating verb (`upload`, `delete`, `copy`, `move`) that **succeeded**. Absent on reads, on failures, on `signedUploadUrl`, and on every instance with receipts off — so an existing `onAction` consumer that never opted in sees the exact payload it always has. See {@link FilesOptions.receipts}.

TypeReceipt

Was this page helpful?