Skip to main content
Wildo.ai Coming soon

Files and storage

Choose where each field keeps its files

A field declares an ordered set of allowed storage destinations; new uploads use the first configured match.

Two file fields on the same record use different declared storage destinations.

Choose where each field keeps its files

Different documents can need different storage. Wildo lets a file field name the destinations it accepts, instead of forcing every attachment in the application into one place.

The router selects a configured destination from that list. Once a file is stored, its own metadata remembers the provider and reference needed to retrieve it.

Example — Keep one attachment type on a local volume

A deployment stores ordinary attachments in managed object storage but restricts a particular field to the local directory provider. If that directory is not configured, uploads to the field fail instead of quietly landing in the default object store.

For engineers

Use the field to state allowed destinations

Declare storageAccepted on z_file, using the FileStorageAccepted vocabulary. For byte uploads, list order is preference order. Omitting the list, or supplying an empty one, selects Wildo-managed storage and requires that provider to be configured.

The routing loop in file-storage-router.backend.service.ts selects only a registered, configured match. The earlier managed-default branch is omitted; the explicit-list failure is retained.

for (const preferred of storageAccepted) {
  const provider = this.providerMap.get(preferred);
  if (provider && provider.isConfigured()) {
    return provider;
  }
}

const configuredProviders = Array.from(this.providerMap.entries())
  .filter(([, p]) => p.isConfigured())
  .map(([id]) => id);

throw this.errorBuilder.buildError(
  ErrorType.CONFIGURATION,
  undefined,
  {
    customMessageReference: ErrorCustomMessageReference.CONFIGURATION,
    context: {
      message: 'None of the requested storage providers are configured. ' +
        'Ensure the corresponding environment variables are set for at least one of the declared providers.',
      requestedProviders: storageAccepted,
      configuredProviders,
    },
  }
);

Separate placement from naming

For managed and local storage, filePathBinding can shape a folder path and file name within the provider’s key composition. Values can be static or derived from the resource context available at upload time. Multi-file naming requires a policy that avoids every file choosing the same leaf; absent that policy, the file ID is used.

The resolver sanitizes authored path segments and warns when it changes them. Google Drive uses the requested file name but does not apply this composed folder layout. Choose storage first, then use only the naming behavior that destination implements.

Plan changes for existing files separately

A changed preference governs new uploads. Download and cleanup resolve the provider recorded on each existing file, so changing the field does not migrate its bytes. If none of the requested providers is configured, routing fails with a configuration error; it does not try an unlisted destination or recover a credential failure by writing somewhere else.

Remote picker references name their connected provider rather than following byte-upload preference order. The backend still checks that the field explicitly accepts the matching storage destination before retaining the reference.

Make placement a field choice and a deployment choice

These illustrative declarations distinguish a preference from a requirement. Both use the public FileStorageAccepted vocabulary:

import { FileStorageAccepted, z_file } from '@wildo-ai/zod-decorators';

const attachment = z_file({
  storageAccepted: [
    FileStorageAccepted.WILDO_MANAGED,
    FileStorageAccepted.LOCAL_DIRECTORY,
  ],
});

const localDocument = z_file({
  storageAccepted: [FileStorageAccepted.LOCAL_DIRECTORY],
});

The first field allows managed storage, then local storage if managed storage is not configured. The second permits only the configured local directory. Provision the destination through the application’s environment setup: managed storage uses STORAGE_ENDPOINT and its platform-issued credential path; local storage uses STORAGE_LOCAL_BASE_DIR, which must point to a volume the backend can actually read and write. A configured value is not a successful connectivity or filesystem-permission check.

Configured deploymentManaged-first fieldLocal-only field
Managed and localManaged storageLocal directory
Only localLocal directoryLocal directory
Only managedManaged storageConfiguration refusal
NeitherConfiguration refusalConfiguration refusal

An omitted or empty storageAccepted list means managed storage, not this two-destination fallback. If managed storage is configured but its write fails, the router does not retry that upload into local storage: preference selection happens before the write.

Existing files retain their recorded provider and storage handle. Changing this declaration affects future placement; it neither relocates earlier bytes nor changes the connected-provider matching used for remote picker references.

Building a B2B product or an internal tool?

Wildo is not self-service yet. Tell us what you have in mind and we will say plainly whether it fits, and what happens next.