NestboltNestbolt

@nestbolt/medialibrary

Introduction

File uploads and media management for NestJS -- associate files with entities, generate image conversions, and store on local disk or S3.

@nestbolt/medialibrary is a full-featured media management package for NestJS applications. It associates uploaded files with TypeORM entities, generates image conversions using Sharp, and stores files on local disk or AWS S3-compatible object storage.

Key Features

  • Multi-source uploads -- Upload files from file paths, buffers, streams, URLs, or base64-encoded strings through a unified, chainable API.
  • Entity association -- Link any number of media files to any TypeORM entity using a model type and ID pair. No schema changes required on your existing tables.
  • Media collections -- Group files into named collections with configurable constraints such as MIME type restrictions, maximum file sizes, single-file enforcement, and size limits.
  • Image conversions -- Automatically generate thumbnails, previews, and other image variants when media is uploaded. Powered by Sharp with support for resize, crop, format conversion, quality, blur, sharpen, rotate, flip, greyscale, and arbitrary Sharp operations.
  • Multiple storage backends -- Store originals and conversions on local disk or AWS S3 (including S3-compatible services like MinIO, DigitalOcean Spaces, and Cloudflare R2). Each collection can use a different disk.
  • Presigned URLs -- Generate time-limited signed URLs for private S3 objects.
  • Custom properties -- Attach arbitrary JSON metadata to any media record with full dot-notation support for nested keys.
  • Event system -- React to media lifecycle events (added, deleted, collection cleared, conversion started/completed/failed) via @nestjs/event-emitter.
  • Entity mixin -- Add media methods directly to your TypeORM entities for a convenient, object-oriented API.
  • Automatic cleanup -- Media files are automatically deleted from storage when the associated entity is removed.
  • Customizable path, URL, and file naming -- Plug in your own PathGenerator, UrlGenerator, or FileNamer implementations.

Architecture

The package is built around several core components:

MediaModule registers all providers globally. You call MediaModule.forRoot() or MediaModule.forRootAsync() once in your root module, and MediaService becomes available everywhere via dependency injection.

MediaService is the primary API surface. It provides factory methods that return FileAdder instances for uploading, and query methods for retrieving media records and generating URLs.

FileAdder implements a fluent builder pattern. You chain configuration calls (.forModel(), .usingName(), .toDisk(), .withCustomProperties(), etc.) and finalize the upload by calling .toMediaCollection().

MediaEntity is a TypeORM entity stored in a media table. It tracks the file's location, disk, collection, MIME type, size, custom properties, and which conversions have been generated.

Decorators (@HasMedia, @RegisterMediaCollections, @RegisterMediaConversions) let you declare collection constraints and image conversions directly on your entity classes.

HasMediaMixin adds convenience methods (addMedia(), getMedia(), getFirstMedia(), etc.) to entity instances so you can work with media without injecting MediaService.

DiskManager resolves named disk configurations to StorageDriver instances. The built-in drivers are LocalDriver and S3Driver, but you can implement the StorageDriver interface for custom backends.

FileManipulator performs image conversions by piping through Sharp operations defined in ConversionBuilder.

MediaSubscriber is a TypeORM subscriber that automatically deletes all associated media files when an entity decorated with @HasMedia() is removed.

Basic Usage

import { MediaService } from "@nestbolt/medialibrary";

@Injectable()
export class PostService {
  constructor(private readonly mediaService: MediaService) {}

  async uploadPhoto(postId: string, file: Buffer): Promise<void> {
    await this.mediaService
      .addMediaFromBuffer(file, "photo.jpg")
      .forModel("Post", postId)
      .withCustomProperties({ alt: "A sunset photo" })
      .toMediaCollection("images");
  }

  async getPhotoUrl(postId: string): Promise<string> {
    const media = await this.mediaService.getFirstMedia("Post", postId, "images");
    if (!media) return "";
    return this.mediaService.getUrl(media, "thumbnail");
  }
}

Next Steps

  • Installation -- Install the package and its peer dependencies.
  • Quick Start -- Set up the module, upload a file, and retrieve its URL in minutes.
  • Configuration -- All forRoot and forRootAsync options explained.
  • Adding Media -- Every upload method and FileAdder option in detail.
  • Collections -- Define collection rules and constraints on your entities.
  • Image Conversions -- Auto-generate thumbnails and image variants.
  • Querying Media -- Retrieve, manage, reorder, and delete media.
  • Storage Drivers -- Configure local, S3, and custom storage backends.
  • Events -- Listen to media lifecycle events.