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

@vsaas/loopback-component-storage

v11.0.2

Published

Fork of LoopBack component for file storage and management using AWS S3 or compatible services

Readme

LoopBack Component Storage

Modernized fork of loopback-component-storage for LoopBack 3 projects that only need:

  • filesystem
  • s3 / aws / amazon

This fork removes pkgcloud, formidable, strong-globalize, and the legacy provider matrix. The package is now implemented in TypeScript and keeps the LoopBack 3 connector surface.

Stack

  • TypeScript 6.0.2
  • @fastify/busboy for streaming multipart parsing
  • AWS SDK for JavaScript v3 for S3
  • tsdown for builds
  • vitest for tests
  • oxlint for linting

Behavior

  • Uploads are streamed end to end.
  • When maxFileSize is exceeded, the request is aborted immediately.
  • Filesystem uploads do not buffer the full body in memory.
  • S3 uploads use AWS SDK v3 multipart upload support.
  • Signed URLs validate object existence before signing by default. Set signedUrl.validateBeforeSign to false to skip the extra HeadObject request.
  • Filesystem signed URLs are local application URLs signed with HMAC. They require signedUrl.secret.

Supported upload options

  • getFilename
  • nameConflict: "makeUnique"
  • allowedContentTypes
  • maxFileSize
  • maxFieldsSize
  • acl
  • StorageClass
  • CacheControl
  • ServerSideEncryption
  • SSEKMSKeyId
  • SSECustomerAlgorithm
  • SSECustomerKey
  • SSECustomerKeyMD5

S3 client tuning

For S3 datasources you can also tune the AWS SDK v3 client:

  • maxAttempts
  • retryMode
  • connectionTimeout
  • requestTimeout
  • socketTimeout
  • throwOnRequestTimeout
  • maxSockets

Example:

{
  "name": "storage",
  "connector": "@vsaas/loopback-component-storage",
  "provider": "s3",
  "region": "us-east-1",
  "maxAttempts": 3,
  "retryMode": "standard",
  "connectionTimeout": 2000,
  "requestTimeout": 30000,
  "socketTimeout": 30000,
  "throwOnRequestTimeout": true,
  "maxSockets": 50
}

Datasource examples

Filesystem:

{
  "name": "storage",
  "connector": "@vsaas/loopback-component-storage",
  "provider": "filesystem",
  "root": "./storage",
  "maxFileSize": 10485760,
  "maxFieldsSize": 1048576,
  "signedUrl": {
    "enabled": true,
    "expiresIn": 900,
    "secret": "replace-this-with-a-long-random-secret",
    "baseUrl": "https://api.example.com"
  }
}

AWS S3:

{
  "name": "storage",
  "connector": "@vsaas/loopback-component-storage",
  "provider": "s3",
  "region": "us-east-1",
  "accessKeyId": "YOUR_ACCESS_KEY",
  "secretAccessKey": "YOUR_SECRET_KEY",
  "CacheControl": "max-age=300",
  "signedUrl": {
    "enabled": true,
    "expiresIn": 900,
    "strategy": "provider",
    "validateBeforeSign": true
  },
  "maxAttempts": 3,
  "retryMode": "standard",
  "connectionTimeout": 2000,
  "requestTimeout": 30000,
  "socketTimeout": 30000,
  "throwOnRequestTimeout": true,
  "maxSockets": 50
}

MinIO or local S3-compatible storage:

{
  "name": "storage",
  "connector": "@vsaas/loopback-component-storage",
  "provider": "amazon",
  "region": "us-east-1",
  "endpoint": "http://localhost:9000",
  "forcePathStyle": true,
  "keyId": "MINIO_ACCESS_KEY",
  "key": "MINIO_SECRET_KEY",
  "CacheControl": "max-age=300",
  "signedUrl": {
    "enabled": true,
    "expiresIn": 1800,
    "strategy": "provider",
    "validateBeforeSign": false
  },
  "requestTimeout": 20000
}

Dedicated datasource with signed URLs enabled:

{
  "AssetStorage": {
    "name": "AssetStorage",
    "connector": "@vsaas/loopback-component-storage",
    "provider": "amazon",
    "region": "us-west-1",
    "endpoint": "http://localhost:9000",
    "forcePathStyle": true,
    "keyId": "${S3_ACCESS_KEY_ID_WEST_1}",
    "key": "${S3_SECRET_ACCESS_KEY_WEST_1}",
    "CacheControl": "max-age=300",
    "signedUrl": {
      "enabled": true,
      "expiresIn": 1800,
      "strategy": "provider",
      "validateBeforeSign": true
    },
    "maxAttempts": 2,
    "requestTimeout": 20000
  }
}

signedUrl is only used when the download request includes ?signedUrl=true or ?signed-url=true.

For S3-compatible providers:

  • signedUrl.strategy: "provider" keeps the current behavior and returns a presigned URL from S3 or the compatible backend
  • signedUrl.strategy: "local" returns a signed URL from your own app instead of exposing the backend URL; that URL hits the component and streams the file through your server
  • when signedUrl.strategy is "local", signedUrl.secret is required because the component signs and validates the local URL itself
  • you can override the datasource strategy per request via signedUrlStrategy=local|provider or signed-url-strategy=local|provider
  • when calling getSignedUrl() or download() from code, you can pass an options object like { strategy: "local" }; that explicit option has higher priority than both the query string and the datasource configuration

For filesystem providers:

  • signedUrl.secret is required to generate and validate local signed URLs
  • signedUrl.baseUrl is optional and lets you force the public origin used in generated URLs when the app is behind a proxy
  • signed filesystem URLs now prefer /:container/signed-download?file=... with expires and signature query params
  • /:container/download/:file(*) and /:container/signed-download/:file(*) are still supported for compatibility
  • the current request query string is preserved except for signing-related and sensitive params such as signedUrl, signed-url, signature, expires, and anything that looks like tokens, secrets, passwords, authorization, API keys, or AWS presign parameters

This allows you to keep /:container/download protected and expose only /:container/signed-download as a public route that validates the signature before streaming the file. The target file can be provided as a query string like ?file=projects/project-1/event-summaries/2026-03-29.json.gz.

Example S3 datasource that always returns app-local signed URLs:

{
  "name": "storage",
  "connector": "@vsaas/loopback-component-storage",
  "provider": "amazon",
  "region": "us-east-1",
  "endpoint": "http://localhost:9000",
  "forcePathStyle": true,
  "keyId": "MINIO_ACCESS_KEY",
  "key": "MINIO_SECRET_KEY",
  "signedUrl": {
    "enabled": true,
    "expiresIn": 1800,
    "strategy": "local",
    "secret": "replace-this-with-a-long-random-secret",
    "baseUrl": "https://api.example.com",
    "validateBeforeSign": true
  }
}

Migration notes from legacy pkgcloud-style configs

  • forcePathBucket -> forcePathStyle
  • maxRetries -> maxAttempts
  • defaultUploadParams.CacheControl -> CacheControl
  • signatureVersion is no longer used
  • protocol is no longer used; include the protocol directly in endpoint
  • httpOptions.timeout -> requestTimeout
  • httpOptions.agent.keepAlive is not needed because AWS SDK v3 already reuses connections by default

When using custom S3-compatible endpoints, endpoint should usually point to the service root, for example http://localhost:9000, not to a bucket URL such as http://localhost:9000/my-bucket.

Development

npm run lint
npm run typecheck
npm run build
npm test