CodexaCodexa

Registry

Named storage managers, useful when a project uploads to more than one provider or bucket.

createStorageRegistry() holds several independently configured storage managers under a name, useful when different kinds of assets go to different places, avatars on ImageKit, invoices on S3, for example.

import { createStorageRegistry } from '@codexa/core/storage';

const storages = createStorageRegistry();

const mediaStorage = storages.register('media', cloudinaryConfig);
const invoiceStorage = storages.register('invoices', s3Config);

await mediaStorage.upload(imageBytes, { folder: 'products' });
await invoiceStorage.upload(pdfBytes, { folder: 'invoices' });

Once registered, storages.get('media') from anywhere else in your code reaches the same manager, without threading it through function arguments or module-level exports by hand.

Reading the registry back

storages.has('media');   // boolean
storages.names();        // readonly string[]
storages.get('media');   // throws if not registered
storages.register('media', cloudinaryConfig);
storages.register('media', cloudinaryConfig); // throws: Storage manager "media" is already registered.

Registering the same name twice throws rather than silently replacing the first manager, the same rule Store and Event Bus registries follow.

Registering a custom adapter

register takes an optional third argument, an adapter instance, that bypasses provider auto-resolution entirely. Useful for a custom S3-compatible service buildStorageConfig does not have a preset for, or for swapping in a fake adapter in tests.

const invoiceStorage = storages.register('invoices', s3Config, new MyMinioAdapter(s3Config));

No connections to close

storages.remove('media'); // drops the reference, returns the manager if it existed
storages.clear();         // drops every reference

Unlike a store or event bus registry, there is no close or closeAll here. A StorageManager does not hold a long-lived connection of its own, remove and clear only forget the registry's reference to it. If you built a custom adapter that does own a connection, such as a hand-rolled MinIO client, manage that connection's lifecycle in the plugin or app that created it, the same ownership rule covered in Resource Composition.

On this page