npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@azversan/storage

v1.1.0

Published

A flexible NestJS storage module with support for Local, GCS, and S3 drivers

Readme

@azversan/storage

A flexible, production-ready NestJS storage module with built-in support for Local, Google Cloud Storage (GCS), and AWS S3 (and S3-compatible) drivers.

npm version license


Features

  • 🗂️ Three built-in drivers — Local filesystem, AWS S3 (+ MinIO / Cloudflare R2), Google Cloud Storage
  • Dynamic NestJS module — synchronous and asynchronous registration patterns
  • 🔢 Batch registrationregisterMany() / registerManyAsync() to wire multiple storages in one call
  • 🔑 Injection decorator@InjectStorage('name') for clean constructor injection
  • 🗃️ Registry serviceStorageRegistry to resolve any driver by name at runtime
  • 🔒 Signed URLs — temporary pre-signed access URLs for private objects (S3 & GCS)
  • 🧩 Extensible — extend StorageDriver to build your own backend

Installation

npm install @azversan/storage

Required peer dependencies:

npm install @nestjs/common @nestjs/core reflect-metadata rxjs

Optional peer dependencies — install only the drivers you need:

# AWS S3 / MinIO / Cloudflare R2
npm install @aws-sdk/client-s3 @aws-sdk/s3-request-presigner

# Google Cloud Storage
npm install @google-cloud/storage

Quick Start

Local Driver

import { Module } from '@nestjs/common';
import { StorageModule, LocalStorageDriver } from '@azversan/storage';

@Module({
  imports: [
    StorageModule.register({
      name: 'avatars',
      driverClass: LocalStorageDriver,
      options: {
        dest: './uploads/avatars',
        baseUrl: 'http://localhost:3000/uploads/avatars',
      },
    }),
  ],
})
export class AppModule {}

AWS S3 Driver

import { Module } from '@nestjs/common';
import { StorageModule, S3StorageDriver } from '@azversan/storage';

@Module({
  imports: [
    StorageModule.register({
      name: 'documents',
      driverClass: S3StorageDriver,
      options: {
        region: 'us-east-1',
        bucket: 'my-documents-bucket',
        accessKeyId: process.env.AWS_ACCESS_KEY_ID,
        secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY,
      },
    }),
  ],
})
export class AppModule {}

MinIO / S3-compatible:

StorageModule.register({
  name: 'minio',
  driverClass: S3StorageDriver,
  options: {
    region: 'us-east-1',
    bucket: 'my-bucket',
    endpoint: 'http://localhost:9000',
    forcePathStyle: true,
    accessKeyId: 'minioadmin',
    secretAccessKey: 'minioadmin',
  },
});

Google Cloud Storage Driver

import { Module } from '@nestjs/common';
import { StorageModule, GcsStorageDriver } from '@azversan/storage';

@Module({
  imports: [
    StorageModule.register({
      name: 'media',
      driverClass: GcsStorageDriver,
      options: {
        projectId: 'my-gcp-project',
        bucket: 'my-media-bucket',
        keyFilename: '/path/to/service-account.json',
        publicRead: true,
      },
    }),
  ],
})
export class AppModule {}

Registration Methods

register()

Register a single storage driver synchronously.

StorageModule.register({
  name: 'avatars',
  driverClass: LocalStorageDriver,
  options: { dest: './uploads' },
});

registerMany()

Register multiple drivers in a single call.

StorageModule.registerMany([
  {
    name: 'avatars',
    driverClass: LocalStorageDriver,
    options: { dest: './uploads/avatars' },
  },
  {
    name: 'documents',
    driverClass: S3StorageDriver,
    options: { region: 'us-east-1', bucket: 'docs-bucket' },
  },
]);

registerAsync()

Register a driver asynchronously — useful when options come from ConfigService or another async provider.

useFactory:

StorageModule.registerAsync({
  name: 'avatars',
  driverClass: S3StorageDriver,
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    region: config.get('AWS_REGION'),
    bucket: config.get('S3_BUCKET'),
    accessKeyId: config.get('AWS_ACCESS_KEY_ID'),
    secretAccessKey: config.get('AWS_SECRET_ACCESS_KEY'),
  }),
});

useClass:

@Injectable()
class StorageConfigFactory implements StorageOptionsFactory<S3Options> {
  constructor(private readonly config: ConfigService) {}

  createStorageOptions(): S3Options {
    return {
      region: this.config.get('AWS_REGION'),
      bucket: this.config.get('S3_BUCKET'),
    };
  }
}

StorageModule.registerAsync({
  name: 'avatars',
  driverClass: S3StorageDriver,
  useClass: StorageConfigFactory,
});

useExisting:

StorageModule.registerAsync({
  name: 'avatars',
  driverClass: S3StorageDriver,
  imports: [ConfigFactoryModule],
  useExisting: StorageConfigFactory,
});

registerManyAsync()

Register multiple drivers asynchronously in a single call.

StorageModule.registerManyAsync([
  {
    name: 'avatars',
    driverClass: LocalStorageDriver,
    useFactory: () => ({ dest: './uploads/avatars' }),
  },
  {
    name: 'backups',
    driverClass: GcsStorageDriver,
    imports: [ConfigModule],
    inject: [ConfigService],
    useFactory: (config: ConfigService) => ({
      projectId: config.get('GCS_PROJECT_ID'),
      bucket: config.get('GCS_BUCKET'),
    }),
  },
]);

Injecting a Driver

Using @InjectStorage()

Inject a named driver directly into a constructor using the @InjectStorage() parameter decorator.

import { Injectable } from '@nestjs/common';
import { InjectStorage, LocalStorageDriver } from '@azversan/storage';

@Injectable()
export class AvatarService {
  constructor(@InjectStorage('avatars') private readonly storage: LocalStorageDriver) {}

  async uploadAvatar(userId: string, buffer: Buffer): Promise<string> {
    const result = await this.storage.upload({
      key: `avatars/${userId}.png`,
      buffer,
      mimeType: 'image/png',
    });
    return result.url ?? result.key;
  }
}

Using StorageRegistry

Resolve any registered driver by name at runtime using the StorageRegistry service.

import { Injectable } from '@nestjs/common';
import { StorageRegistry, S3StorageDriver } from '@azversan/storage';

@Injectable()
export class FileService {
  constructor(private readonly storageRegistry: StorageRegistry) {}

  getDriver(name: string) {
    return this.storageRegistry.get<S3StorageDriver>(name);
  }
}

Note: StorageRegistry is globally provided once StorageModule has been imported in your root AppModule. No additional imports are needed in feature modules.

Driver API

All drivers extend StorageDriver and implement the following interface.

upload()

Upload a raw buffer to the storage backend.

const result = await storage.upload({
  key: 'images/photo.jpg',
  buffer: fileBuffer,
  mimeType: 'image/jpeg',
  metadata: { uploadedBy: 'user-42' },
});

// result: { key, driver, size, url? }

delete()

Delete an object by its storage key.

await storage.delete('images/photo.jpg');

exists()

Check whether an object exists at the given key. Returns true or false.

const found = await storage.exists('images/photo.jpg');

getSignedUrl()

Generate a temporary pre-signed URL for private object access. Available on S3StorageDriver and GcsStorageDriver.

// S3: ttl in seconds (default: 900 = 15 min)
const url = await s3Driver.getSignedUrl('private/report.pdf', 300);

// GCS: ttl in milliseconds (default: 900_000 = 15 min)
const url = await gcsDriver.getSignedUrl('private/report.pdf', 300_000);

Driver Reference

LocalOptions

| Option | Type | Required | Description | | --------- | -------- | -------- | -------------------------------------------------------------- | | dest | string | ✅ | Absolute or relative path where files will be stored on disk. | | baseUrl | string | ❌ | Base URL prepended to the key to build a public URL on upload. |

S3Options

| Option | Type | Required | Description | | ----------------- | --------- | -------- | ------------------------------------------------------------------------------------ | | region | string | ✅ | AWS region (e.g. us-east-1). | | bucket | string | ✅ | S3 bucket name. | | accessKeyId | string | ❌ | AWS access key ID. Falls back to environment / IAM role if omitted. | | secretAccessKey | string | ❌ | AWS secret access key. Falls back to environment / IAM role if omitted. | | endpoint | string | ❌ | Custom endpoint for S3-compatible services (e.g. http://localhost:9000 for MinIO). | | forcePathStyle | boolean | ❌ | Force path-style URLs. Required for MinIO. Defaults to false. | | publicRead | boolean | ❌ | Tag uploads as public-read and return a permanent URL. Defaults to false. |

GcsOptions

| Option | Type | Required | Description | | ------------- | --------- | -------- | ------------------------------------------------------------------------------------- | | projectId | string | ✅ | GCP project ID. | | bucket | string | ✅ | GCS bucket name. | | keyFilename | string | ❌ | Path to a service-account key file. Use this or credentials, not both. | | credentials | object | ❌ | Inline service-account credentials. Use this or keyFilename, not both. | | publicRead | boolean | ❌ | Make uploaded objects world-readable and return a permanent URL. Defaults to false. |

Writing a Custom Driver

Extend StorageDriver to implement any backend you need.

import { StorageDriver, UploadOptions, UploadResult } from '@azversan/storage';

export interface MyOptions {
  endpoint: string;
  token: string;
}

export class MyCustomDriver extends StorageDriver<MyOptions> {
  readonly driver = 'my-custom';

  async upload(options: UploadOptions): Promise<UploadResult> {
    // your upload logic here
    return { key: options.key, driver: this.driver, size: options.buffer.length };
  }

  async delete(key: string): Promise<void> {
    // your delete logic here
  }

  async exists(key: string): Promise<boolean> {
    // your exists check here
    return false;
  }
}

Then register it like any other driver:

StorageModule.register({
  name: 'custom',
  driverClass: MyCustomDriver,
  options: { endpoint: 'https://my-storage.io', token: process.env.MY_TOKEN },
});

Scripts

# Build the package
npm run build

# Build in watch mode
npm run build:watch

# Run all tests
npm run test

# Run tests in watch mode
npm run test:watch

# Run tests with coverage report
npm run test:cov

# Run ESLint (with auto-fix)
npm run lint

# Format source files with Prettier
npm run format

Project Structure

src/
└── storage/
    ├── drivers/
    │   ├── __tests__/
    │   │   ├── gcs.driver.spec.ts
    │   │   ├── local.driver.spec.ts
    │   │   └── s3.driver.spec.ts
    │   ├── gcs.driver.ts
    │   ├── local.driver.ts
    │   └── s3.driver.ts
    ├── services/
    │   └── registry/
    │       └── registry.service.ts
    ├── __tests__/
    │   ├── storage.decorator.spec.ts
    │   └── storage.module.spec.ts
    ├── storage.constants.ts
    ├── storage.decorator.ts
    ├── storage.interface.ts
    ├── storage.module.ts
    └── storage.utils.ts

Support

If this project helps you or your team, please consider supporting its continued development. Your support helps maintain the package, improve documentation, and keep the project actively maintained. 🚀

Ways to Support

Thank you for supporting open-source software and helping this project grow ❤️

License

MIT License © Agung Dirgantara