Skip to main content

Quik Framework :: Storage Express

Express helpers for handling local file uploads via Multer-backed middleware.

Installation

npm install @quik/storage-express

Configuration

This package relies on the local storage settings from @quik/storage:

  • STORAGE_LOCAL_ENABLED
  • STORAGE_LOCAL_UPLOAD (temporary upload folder)
  • STORAGE_LOCAL_DESTINATION (final storage folder)
  • STORAGE_LOCAL_SIGNED_SECRET (required to serve/accept signed local storage URLs)
  • STORAGE_LOCAL_HTTP_BASE_PATH (default: /storage/local; base path for the signed download/upload routes)

Usage

Enable the module in your bootstrap setup and attach upload decorators:

import { LocalStorage } from '@quik/storage-express';
import { Decorators, QRoute } from '@quik/http';

@Decorators.Route.Route('/uploads')
export class UploadRoute extends QRoute {
@LocalStorage.Decorators.SingleFile('file')
@Decorators.Endpoint.POST('/')
async upload(event) {
return { file: event.context.files?.file };
}
}

The middleware converts Multer files into LocalStorageFile entities and exposes them on the Express context (context.files).

Persisting uploads

Use the persist decorators to move files into the configured destination via the storage engine:

import { LocalStorage } from '@quik/storage-express';
import { Decorators, QRoute } from '@quik/http';

@Decorators.Route.Route('/uploads')
export class UploadRoute extends QRoute {
@LocalStorage.Decorators.SingleFilePersist('file')
@Decorators.Endpoint.POST('/persist')
async uploadAndPersist(event) {
return { file: event.context.files?.file };
}
}

SingleFilePersist and ArrayFilePersist accept an optional destination path to override the configured STORAGE_LOCAL_DESTINATION.

Notes:

  • LocalStorageFile.moveToDestination is async.
  • LocalStorageFile.extension includes the leading dot (e.g. .txt).

Middleware Options & Limits

  • SingleFile(fieldName) accepts exactly one file for the given field.
  • ArrayFile(fieldName, maxCount) accepts multiple files for one field and forwards maxCount to Multer.
  • SingleFilePersist and ArrayFilePersist move uploads into STORAGE_LOCAL_DESTINATION, or an explicit destination.

Multer is configured with the STORAGE_LOCAL_UPLOAD temp folder. Size limits and file filters should be applied by composing additional Express middleware when needed.

Signed download/upload endpoints

This package does not register GET/POST routes for signed local storage URLs itself — the consuming application defines those routes (path, method, request/response shape are application-specific). What this package provides is the building block for verifying the token:

  • @quik/storage's QLocalStorageEngine.getSignedDownloadURL/getSignedUploadURL (or PresignedLocalURLEntity) produce a URL of the form {STORAGE_LOCAL_HTTP_BASE_PATH}/{download|upload}?token=.... STORAGE_LOCAL_HTTP_BASE_PATH is only used to build that URL string — mount your route at a matching path if you want the two to line up.
  • LocalStorage.Decorators.RequireSignedToken(purpose) (alias for RequireLocalStorageToken, also exported directly) is an endpoint middleware decorator that verifies the token query parameter and throws before the handler runs if it's missing, malformed, expired, or issued for the wrong purpose — the same shape as @quik/authorization's AuthDecorators.CanAccess. On success it stores the verified storage path on the event via event.getObject(LOCAL_STORAGE_TOKEN_PATH_KEY).
import { Decorators, QQueryParameters, QRoute, Responses } from '@quik/http';
import { Fields } from '@quik/entity';
import { LOCAL_STORAGE_TOKEN_PATH_KEY, LocalStorage, RequireLocalStorageToken } from '@quik/storage-express';
import { QStorageEngineType, StorageStore } from '@quik/storage';
import type { QLocalStorageEngine } from '@quik/storage';

@Decorators.Parameters.Query()
class TokenQuery extends QQueryParameters {
@Fields.String()
public token: string;
}

@Decorators.Route.API('/storage/local')
export class LocalStorageRoute extends QRoute {
@Decorators.Endpoint.GET('/download', { query: TokenQuery })
@RequireLocalStorageToken('download')
async download(event) {
const path = event.getObject<string>(LOCAL_STORAGE_TOKEN_PATH_KEY);
const engine = StorageStore.get(QStorageEngineType.LOCAL) as QLocalStorageEngine;
const file = await engine.get(path);
return Responses.OKFile(file.path, file.mimeType);
}
}

RequireLocalStorageToken throws StorageInvalidSignedURLError (missing/malformed token, 401), StorageSignedURLExpiredError (410), or StorageSignedURLPurposeMismatchError (401) — all from @quik/storage. There is no Bearer authentication involved; the signed token itself is the authorization.

API Highlights

  • LocalStorage.init() wires Multer if local storage is enabled.
  • LocalStorage.Decorators provides upload decorators, plus RequireSignedToken for verifying signed local storage tokens.
  • RequireLocalStorageToken/LOCAL_STORAGE_TOKEN_PATH_KEY — also exported directly for use outside the LocalStorage namespace.
  • LocalStorageFile entity represents uploaded files.
  • Persist decorators move uploads into storage using the local engine.

Testing & Coverage

See the root instructions for details on running pnpm run test:coverage and accessing coverage artifacts.

API Reference

Generated API documentation is available in the storage-express API section.