Aviary
Reference

Configuration

Every MediaModule option, the MediaService surface, and the injection tokens — in one place.

The complete configuration surface for @dudousxd/nestjs-media.

MediaModule.forRoot(options)

interface MediaModuleOptions {
  // ── storage (layer 1) ──────────────────────────────────────────────
  default: string;                          // name of the default disk
  disks: Record<string, StorageDriver>;     // named disks

  // ── media-library (layer 2) — enabled when `store` is present ───────
  store?: MediaStore;
  collections?: MediaCollectionConfig[];
  imageProcessor?: ImageProcessor;

  // ── resumable uploads — enabled when `uploadSessions` is present ─────
  uploadSessions?: UploadSessionStore;
  uploadTmpPrefix?: string;                 // default '.uploads'
  tus?: {                                   // mounts the tus HTTP controller
    disk: string;
    basePath?: string;                      // default '/media/uploads'
    maxSize?: number;                       // reject larger creations (bytes)
    keyFor?: (filename: string, token: string, metadata: Record<string, string>) => string;
  };

  // ── direct (presigned multipart) uploads — needs an S3-capable disk ──
  direct?: {                                // mounts the direct upload controller
    disk: string;
    basePath?: string;
    partSize?: number;                      // default 8 MiB
  };
}
OptionRequiredPurpose
defaultWhich disk media.disk() returns with no name
disksThe named disks (local, s3, custom, …)
storeEnables media.library; omit for raw-storage-only
collectionsPer-collection rules + conversions
imageProcessorRequired only if any collection declares conversions
uploadSessionsEnables media.uploads (the resumable engine)
uploadTmpPrefixWhere in-progress chunks are staged on the disk
tusMounts MediaUploadController (needs uploadSessions)
directEnables media.directUploads + mounts MediaDirectUploadController (needs a presign/multipart disk, e.g. S3)

MediaModule.forRootAsync(options)

Same options, resolved through a factory with DI:

interface MediaModuleAsyncOptions {
  imports?: any[];
  inject?: any[];
  useFactory: (...args: any[]) => MediaModuleOptions | Promise<MediaModuleOptions>;
}

Use this whenever a disk or store needs an injected dependency (an S3Client, a DataSource, config service).

MediaCollectionConfig

interface MediaCollectionConfig {
  name: string;
  single?: boolean;            // attaching replaces existing media
  disk?: string;               // override the target disk for this collection
  acceptsMimeTypes?: string[]; // allow-list, else MimeNotAllowedError
  conversions?: ConversionPreset[];
}

interface ConversionPreset {
  name: string;
  width?: number; height?: number;
  fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
  format?: 'jpeg' | 'png' | 'webp' | 'avif';
  quality?: number;
  eager?: boolean;             // generate on attach instead of on first access
}

MediaService

class MediaService {
  disk(name?: string): StorageDriver;       // layer 1
  get library(): MediaLibrary;              // layer 2 — throws if no store configured
  get attachments(): AttachmentManager;     // attachment-as-column API — always available
  get uploads(): ResumableUploadManager;    // resumable proxy — throws if no uploadSessions configured
  get directUploads(): DirectUploadManager; // direct multipart — throws if no direct configured
}

Why the getters throw

library, uploads, and directUploads throw a clear, actionable error when their feature wasn't configured, rather than handing you undefined. The boundary is explicit: if you didn't pass a store, media.library tells you so by name.

MediaLibrary

attach(input: AttachInput): Promise<MediaRecord>;
ensureConversion(id: string, conversion: string): Promise<MediaRecord>;
list(ownerType: string, ownerId: string, collection?: string): Promise<MediaRecord[]>;
delete(id: string): Promise<void>;
url(id: string, conversion?: string): Promise<string>;

interface AttachInput {
  ownerType: string; ownerId: string; collection: string;
  fileName: string; mimeType: string; contents: Buffer | Readable;
  size?: number; name?: string;
  customProperties?: Record<string, unknown>;
  disk?: string;
}

Injection tokens

For advanced wiring, the module also exports tokens you can inject directly:

import { MEDIA_STORAGE, MEDIA_LIBRARY, MEDIA_ATTACHMENTS, MEDIA_UPLOADS, MEDIA_TUS, MEDIA_DIRECT } from '@dudousxd/nestjs-media';
TokenResolves to
MEDIA_STORAGEStorageManager
MEDIA_LIBRARYMediaLibrary | null
MEDIA_ATTACHMENTSAttachmentManager
MEDIA_UPLOADSResumableUploadManager | null
MEDIA_TUSTusUploadHandler | null
MEDIA_DIRECTDirectUploadManager | null

Error codes

Errorcode
FileNotFoundErrorMEDIA_FILE_NOT_FOUND
UnknownDiskErrorMEDIA_UNKNOWN_DISK
UnsupportedOperationErrorMEDIA_UNSUPPORTED_OP
MimeNotAllowedErrorMEDIA_MIME_NOT_ALLOWED
MediaNotFoundErrorMEDIA_RECORD_NOT_FOUND
ConversionNotDefinedErrorMEDIA_CONVERSION_NOT_DEFINED
ImageProcessorMissingErrorMEDIA_IMAGE_PROCESSOR_MISSING
UploadSessionNotFoundErrorMEDIA_UPLOAD_SESSION_NOT_FOUND
UploadOffsetConflictErrorMEDIA_UPLOAD_OFFSET_CONFLICT

On this page