
Security News
ECMAScript 2025 Finalized with Iterator Helpers, Set Methods, RegExp.escape, and More
ECMAScript 2025 introduces Iterator Helpers, Set methods, JSON modules, and more in its latest spec update approved by Ecma in June 2025.
@memberjunction/storage
Advanced tools
This library provides a set of objects that handle the interface between the server-side API and various cloud storage providers.
The @memberjunction/storage
library provides a unified interface for interacting with various cloud storage providers. It abstracts the complexities of different storage services behind a consistent API, making it easy to work with files stored across different cloud platforms.
This library is a key component of the MemberJunction platform, providing seamless file storage operations across multiple cloud providers. It offers a provider-agnostic approach to file management, allowing applications to switch between storage providers without code changes.
FileStorageBase
abstract classFileStorageBase
npm install @memberjunction/storage
This package depends on:
@memberjunction/core
- Core MemberJunction functionality@memberjunction/core-entities
- Entity definitions including FileStorageProviderEntity
@memberjunction/global
- Global utilities and class registrationFirst, configure the environment variables required for your chosen storage provider(s). You can implement multiple providers simultaneously and switch between them based on your application requirements.
Refer to the documentation for each storage provider for detailed configuration requirements (see the "Storage Provider Configuration" section below).
STORAGE_AZURE_CONTAINER=your-container-name
STORAGE_AZURE_ACCOUNT_NAME=your-account-name
STORAGE_AZURE_ACCOUNT_KEY=your-account-key
The library provides high-level utility functions that work with MemberJunction's entity system:
import { createUploadUrl, createDownloadUrl, deleteObject, moveObject } from '@memberjunction/storage';
import { FileStorageProviderEntity } from '@memberjunction/core-entities';
import { Metadata } from '@memberjunction/core';
// Load a FileStorageProviderEntity from the database
async function fileOperationsExample() {
const md = new Metadata();
const provider = await md.GetEntityObject<FileStorageProviderEntity>('File Storage Providers');
await provider.Load('your-provider-id');
// Create pre-authenticated upload URL
const { updatedInput, UploadUrl } = await createUploadUrl(
provider,
{
ID: '123',
Name: 'documents/report.pdf',
ProviderID: provider.ID
}
);
// The client can use the UploadUrl directly to upload the file
console.log(`Upload URL: ${UploadUrl}`);
console.log(`File status: ${updatedInput.Status}`); // 'Uploading'
console.log(`Content type: ${updatedInput.ContentType}`); // 'application/pdf'
// If a ProviderKey was returned, use it for future operations
const fileIdentifier = updatedInput.ProviderKey || updatedInput.Name;
// Later, create pre-authenticated download URL
const downloadUrl = await createDownloadUrl(provider, fileIdentifier);
console.log(`Download URL: ${downloadUrl}`);
// Move the file to a new location
const moved = await moveObject(
provider,
fileIdentifier,
'documents/archived/report_2024.pdf'
);
console.log(`File moved: ${moved}`);
// Delete the file when no longer needed
const deleted = await deleteObject(provider, 'documents/archived/report_2024.pdf');
console.log(`File deleted: ${deleted}`);
}
You can also work directly with a storage provider by instantiating it using the MemberJunction class factory:
import { FileStorageBase } from '@memberjunction/storage';
import { MJGlobal } from '@memberjunction/global';
async function directProviderExample() {
// Create storage instance using the class factory (recommended)
const storage = MJGlobal.Instance.ClassFactory.CreateInstance<FileStorageBase>(
FileStorageBase,
'Azure Blob Storage'
);
// Initialize the storage provider if needed
await storage.initialize();
// List all files in a directory
const result = await storage.ListObjects('documents/');
console.log('Files:', result.objects);
console.log('Directories:', result.prefixes);
// Display detailed metadata for each file
for (const file of result.objects) {
console.log(`\nFile: ${file.name}`);
console.log(` Path: ${file.path}`);
console.log(` Full Path: ${file.fullPath}`);
console.log(` Size: ${file.size} bytes`);
console.log(` Type: ${file.contentType}`);
console.log(` Modified: ${file.lastModified}`);
console.log(` Is Directory: ${file.isDirectory}`);
}
// Create a directory
const dirCreated = await storage.CreateDirectory('documents/reports/');
console.log(`Directory created: ${dirCreated}`);
// Upload a file directly with metadata
const content = Buffer.from('Hello, World!');
const uploaded = await storage.PutObject(
'documents/reports/hello.txt',
content,
'text/plain',
{
author: 'John Doe',
department: 'Engineering',
version: '1.0'
}
);
console.log(`File uploaded: ${uploaded}`);
// Get file metadata without downloading content
const metadata = await storage.GetObjectMetadata('documents/reports/hello.txt');
console.log('File metadata:', metadata);
// Download file content
const fileContent = await storage.GetObject('documents/reports/hello.txt');
console.log('File content:', fileContent.toString('utf8'));
// Copy a file
const copied = await storage.CopyObject(
'documents/reports/hello.txt',
'documents/archive/hello-backup.txt'
);
console.log(`File copied: ${copied}`);
// Check if a file exists
const exists = await storage.ObjectExists('documents/reports/hello.txt');
console.log(`File exists: ${exists}`);
// Check if a directory exists
const dirExists = await storage.DirectoryExists('documents/reports/');
console.log(`Directory exists: ${dirExists}`);
// Delete a directory and all its contents
const dirDeleted = await storage.DeleteDirectory('documents/reports/', true);
console.log(`Directory deleted: ${dirDeleted}`);
}
CreatePreAuthUploadUrlPayload
type CreatePreAuthUploadUrlPayload = {
UploadUrl: string; // Pre-authenticated URL for upload
ProviderKey?: string; // Optional provider-specific key
};
StorageObjectMetadata
type StorageObjectMetadata = {
name: string; // Object name (filename)
path: string; // Directory path
fullPath: string; // Complete path including name
size: number; // Size in bytes
contentType: string; // MIME type
lastModified: Date; // Last modification date
isDirectory: boolean; // Whether this is a directory
etag?: string; // Entity tag for caching
cacheControl?: string; // Cache control directives
customMetadata?: Record<string, string>; // Custom metadata
};
StorageListResult
type StorageListResult = {
objects: StorageObjectMetadata[]; // Files found
prefixes: string[]; // Directories found
};
All storage providers implement these methods:
CreatePreAuthUploadUrl(objectName: string): Promise<CreatePreAuthUploadUrlPayload>
CreatePreAuthDownloadUrl(objectName: string): Promise<string>
MoveObject(oldObjectName: string, newObjectName: string): Promise<boolean>
DeleteObject(objectName: string): Promise<boolean>
ListObjects(prefix: string, delimiter?: string): Promise<StorageListResult>
CreateDirectory(directoryPath: string): Promise<boolean>
DeleteDirectory(directoryPath: string, recursive?: boolean): Promise<boolean>
GetObjectMetadata(objectName: string): Promise<StorageObjectMetadata>
GetObject(objectName: string): Promise<Buffer>
PutObject(objectName: string, data: Buffer, contentType?: string, metadata?: Record<string, string>): Promise<boolean>
CopyObject(sourceObjectName: string, destinationObjectName: string): Promise<boolean>
ObjectExists(objectName: string): Promise<boolean>
DirectoryExists(directoryPath: string): Promise<boolean>
initialize(): Promise<void>
(optional, for async initialization)createUploadUrl<T>(provider: FileStorageProviderEntity, input: T): Promise<{ updatedInput: T & { Status: string; ContentType: string }, UploadUrl: string }>
createDownloadUrl(provider: FileStorageProviderEntity, providerKeyOrName: string): Promise<string>
moveObject(provider: FileStorageProviderEntity, oldProviderKeyOrName: string, newProviderKeyOrName: string): Promise<boolean>
deleteObject(provider: FileStorageProviderEntity, providerKeyOrName: string): Promise<boolean>
The library uses a class hierarchy with FileStorageBase
as the abstract base class that defines the common interface. Each storage provider implements this interface:
FileStorageBase (Abstract Base Class)
āāā AWSFileStorage (@RegisterClass: 'AWS S3')
āāā AzureFileStorage (@RegisterClass: 'Azure Blob Storage')
āāā GoogleFileStorage (@RegisterClass: 'Google Cloud Storage')
āāā GoogleDriveFileStorage (@RegisterClass: 'Google Drive')
āāā SharePointFileStorage (@RegisterClass: 'SharePoint')
āāā DropboxFileStorage (@RegisterClass: 'Dropbox')
āāā BoxFileStorage (@RegisterClass: 'Box')
Classes are registered with the MemberJunction global class factory using the @RegisterClass
decorator, enabling dynamic instantiation based on provider keys.
This library integrates seamlessly with the MemberJunction platform:
FileStorageProviderEntity
from @memberjunction/core-entities
@memberjunction/global
for dynamic provider instantiationEach storage provider requires specific environment variables. Please refer to the official documentation for each provider for detailed information on authentication and additional configuration options.
STORAGE_AWS_BUCKET
: S3 bucket nameSTORAGE_AWS_REGION
: AWS region (e.g., 'us-east-1')STORAGE_AWS_ACCESS_KEY_ID
: AWS access key IDSTORAGE_AWS_SECRET_ACCESS_KEY
: AWS secret access keyFor more information, see AWS S3 Documentation.
STORAGE_AZURE_CONTAINER
: Container nameSTORAGE_AZURE_ACCOUNT_NAME
: Account nameSTORAGE_AZURE_ACCOUNT_KEY
: Account keyFor more information, see Azure Blob Storage Documentation.
STORAGE_GOOGLE_BUCKET
: GCS bucket nameSTORAGE_GOOGLE_KEY_FILE_PATH
: Path to service account key file (JSON)For more information, see Google Cloud Storage Documentation.
STORAGE_GOOGLE_DRIVE_CLIENT_ID
: OAuth client IDSTORAGE_GOOGLE_DRIVE_CLIENT_SECRET
: OAuth client secretSTORAGE_GOOGLE_DRIVE_REDIRECT_URI
: OAuth redirect URISTORAGE_GOOGLE_DRIVE_REFRESH_TOKEN
: OAuth refresh tokenFor more information, see Google Drive API Documentation.
STORAGE_SHAREPOINT_SITE_URL
: SharePoint site URLSTORAGE_SHAREPOINT_CLIENT_ID
: Azure AD client IDSTORAGE_SHAREPOINT_CLIENT_SECRET
: Azure AD client secretSTORAGE_SHAREPOINT_TENANT_ID
: Azure AD tenant IDFor more information, see Microsoft Graph API Documentation.
STORAGE_DROPBOX_ACCESS_TOKEN
: Dropbox access tokenSTORAGE_DROPBOX_REFRESH_TOKEN
: Dropbox refresh token (optional)STORAGE_DROPBOX_APP_KEY
: Dropbox app keySTORAGE_DROPBOX_APP_SECRET
: Dropbox app secretFor more information, see Dropbox API Documentation.
STORAGE_BOX_CLIENT_ID
: Box client IDSTORAGE_BOX_CLIENT_SECRET
: Box client secretSTORAGE_BOX_ENTERPRISE_ID
: Box enterprise IDSTORAGE_BOX_JWT_KEY_ID
: Box JWT key IDSTORAGE_BOX_PRIVATE_KEY
: Box private key (base64 encoded)STORAGE_BOX_PRIVATE_KEY_PASSPHRASE
: Box private key passphrase (optional)For more information, see Box Platform Documentation.
The library is designed to be extensible. To add a new storage provider:
import { FileStorageBase, StorageObjectMetadata, StorageListResult } from '@memberjunction/storage';
import { RegisterClass } from '@memberjunction/global';
@RegisterClass(FileStorageBase, 'My Custom Storage')
export class MyCustomStorage extends FileStorageBase {
protected readonly providerName = 'My Custom Storage';
constructor() {
super();
// Initialize your storage client here
}
public async initialize(): Promise<void> {
// Optional: Perform async initialization
// e.g., authenticate, verify permissions
}
public async CreatePreAuthUploadUrl(objectName: string): Promise<CreatePreAuthUploadUrlPayload> {
// Implement upload URL generation
// Return { UploadUrl: string, ProviderKey?: string }
}
public async CreatePreAuthDownloadUrl(objectName: string): Promise<string> {
// Implement download URL generation
}
// Implement all other abstract methods...
}
If your provider doesn't support certain operations:
public async CreateDirectory(directoryPath: string): Promise<boolean> {
// If directories aren't supported
this.throwUnsupportedOperationError('CreateDirectory');
}
Document required environment variables:
import * as env from 'env-var';
constructor() {
super();
const apiKey = env.get('STORAGE_MYCUSTOM_API_KEY').required().asString();
const endpoint = env.get('STORAGE_MYCUSTOM_ENDPOINT').required().asString();
// Use these to initialize your client
}
Add to src/index.ts
:
export * from './drivers/MyCustomStorage';
Update this README with configuration requirements and any provider-specific notes.
The library provides consistent error handling across all providers:
Thrown when a provider doesn't support a specific operation:
try {
await storage.CreateDirectory('/some/path/');
} catch (error) {
if (error instanceof UnsupportedOperationError) {
console.log(`Provider doesn't support directories: ${error.message}`);
}
}
Each provider may throw errors specific to its underlying SDK. These are not wrapped, allowing you to handle provider-specific error conditions:
try {
await storage.GetObject('non-existent-file.txt');
} catch (error) {
// Handle provider-specific errors
if (error.code === 'NoSuchKey') { // AWS S3
console.log('File not found');
} else if (error.code === 'BlobNotFound') { // Azure
console.log('Blob not found');
}
}
ProviderKey
if returned by CreatePreAuthUploadUrl
documents/
not documents
)initialize()
on providers that require async setupGetObject
and PutObject
methods load entire files into memory; for large files, consider streaming approachesContributions are welcome! To add a new storage provider:
git checkout -b feature/new-provider
)src/drivers/
FileStorageBase
ISC
Part of the MemberJunction platform.
FAQs
This library provides a set of objects that handle the interface between the server-side API and various cloud storage providers.
We found that @memberjunction/storage demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago.Ā It has 4 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
ECMAScript 2025 introduces Iterator Helpers, Set methods, JSON modules, and more in its latest spec update approved by Ecma in June 2025.
Security News
A new Node.js homepage button linking to paid support for EOL versions has sparked a heated discussion among contributors and the wider community.
Research
North Korean threat actors linked to the Contagious Interview campaign return with 35 new malicious npm packages using a stealthy multi-stage malware loader.