Skip to main content
The @talosjs/storage component is a multi-backend file storage layer. Every backend implements the same IStorage interface (put, getFile, getAsJson, delete, list, and more), so you can start on the local filesystem and move to Cloudflare R2 or Bunny while your calling code stays the same. Files are organized into buckets, and the S3-compatible backends share one abstract Storage base class.

One interface across backends

Filesystem, Cloudflare R2, and Bunny all implement IStorage, so you can swap backends without touching callers. Files live under buckets: setBucket() and getBucket() set and read the active one, and you can list or clear a whole bucket in a single call. put() accepts strings, ArrayBufferView, ArrayBuffer, SharedArrayBuffer, Blob, Request/Response, and BunFile/S3File, and you read a file back however you need it: as JSON, as an ArrayBuffer, as a stream, or written straight to disk with getFile(). For bulk uploads, putDir() walks a local directory recursively with an optional regex filter. Register a storage class with a decorator and resolve it from the container.

How it works

You pick (or implement) a backend and register it. Reads and writes go through the IStorage methods; the backend handles the transport, bucketing, and content conversion. The three packaged backends differ in where files live, not in how you call them: FilesystemStorage and CloudflareStorage extend the shared Storage base class and talk to an S3 client under the hood. BunnyStorage implements IStorage directly against the Bunny Storage SDK.

Environment variables

Usage

The API is the same regardless of backend.
Upload a local file or an entire directory, and download objects to disk:
For large files, stream instead of buffering the whole object in memory:

Decorator and usage

@decorator.storage()

Registers a storage class with the container. It accepts an optional scope (defaults to singleton). Use it to register a packaged backend under your own name, or a custom backend that extends Storage or implements IStorage.
Resolve it from the container and inject it where needed:

Exceptions

The component throws StorageException when a backend is misconfigured or an operation fails. It carries a machine-readable key, a human-readable message, and a data object.

Storing files cleanly

Call setBucket() before any read or write, since FilesystemStorage throws STORAGE_BUCKET_REQUIRED otherwise. Group related objects under stable prefixes like avatars/123.png so list() and clearBucket() stay predictable. For anything large, stream with getAsStream() (and putDir/putFile for local sources) instead of loading whole files into memory, and pass a filter regex to putDir() so you only ship the files you mean to. Choose the backend by environment: filesystem for local and dev, Cloudflare R2 or Bunny for production, with the same code either way. Reads throw FILE_NOT_FOUND (or STORAGE_DOWNLOAD_FAILED) when an object is absent, so catch StorageException and branch on error.key. In a custom backend, keep that key constant and put the variable detail in data.

CLI command

Scaffold a storage adapter and its test file with the generator. It writes the class under modules/<module>/src/storage/<Name>Storage.ts and installs @talosjs/storage if it is missing.
The generated class extends Storage as an S3-compatible adapter, ready for you to set the bucket and confirm its credentials:
See storage:create for the full command reference.

Use with Claude and Codex

The generator ships a matching storage:create skill. It runs the scaffold, then guides your AI agent through completing the adapter: setting the bucket, wiring the credential env vars, and finishing the test file. Initialize the skills once for your agent.
Then ask Claude in natural language. It maps the request to the generator, runs it, and fills in the implementation:
Prompt
For example, the prompt above maps to storage:create --name=Upload, then sets the bucket and credential env vars on the generated Storage subclass.