@nodules/linode-storage
v1.0.2
Published
A production-ready, fully-typed Linode Object Storage client, following fs/promises interface that provides seamless cloud operations with enterprise-grade reliability and developer experience.
Maintainers
Readme
Nodules Linode Object Storage
A production-ready, fully-typed Linode Object Storage client, following fs/promises interface that provides seamless cloud operations with enterprise-grade reliability and developer experience.
Installation
npm install @nodules/linode-storageUsage
Simple Factory Pattern
The easiest way to get started - works with any IAsyncFileSystem implementation:
import { FS } from '@nodules/linode-storage';
// Development - local filesystem
const devFs = FS.async.node();
// Testing - in-memory filesystem
const testFs = FS.async.memory();
// Production - Linode Object Storage
const prodFs = FS.async.linode({
region: 'us-southeast-1',
bucket: 'my-bucket',
accessKey: process.env.LINODE_ACCESS_KEY,
secretKey: process.env.LINODE_SECRET_KEY
});
// All share the same interface - swap implementations instantly
await prodFs.writeFile('data.json', JSON.stringify(data));
const content = await prodFs.readFile('data.json');Interface Injection Pattern
Design your services to accept any filesystem implementation:
import { IAsyncFileSystem } from '@nodules/linode-storage';
class DocumentService {
constructor(private fs: IAsyncFileSystem) {}
async saveDocument(id: string, content: string) {
await this.fs.writeFile(`documents/${id}.txt`, content);
}
async loadDocument(id: string): Promise<string> {
return await this.fs.readFile(`documents/${id}.txt`);
}
}
// Development environment
const devService = new DocumentService(FS.async.node());
// Test environment
const testService = new DocumentService(FS.async.memory());
// Production environment
const prodService = new DocumentService(FS.async.linode({
region: 'us-southeast-1',
bucket: 'production-storage',
accessKey: process.env.LINODE_ACCESS_KEY,
secretKey: process.env.LINODE_SECRET_KEY
}));Environment-Based Configuration
import { FS } from '@nodules/linode-storage';
function createFileSystem() {
switch (process.env.NODE_ENV) {
case 'test':
return FS.async.memory();
case 'development':
return FS.async.node();
case 'production':
return FS.async.linode({
region: process.env.LINODE_REGION!,
bucket: process.env.LINODE_BUCKET!,
accessKey: process.env.LINODE_ACCESS_KEY!,
secretKey: process.env.LINODE_SECRET_KEY!
});
default:
throw new Error('Unknown environment');
}
}
const fs = createFileSystem();
await fs.writeFile('config.json', '{}');Direct Class Usage
For advanced configuration, use the class directly:
import { LinodeObjectStorageFileSystem } from '@nodules/linode-storage';
const storage = new LinodeObjectStorageFileSystem({
region: 'us-southeast-1',
bucket: 'my-bucket',
accessKey: process.env.LINODE_ACCESS_KEY,
secretKey: process.env.LINODE_SECRET_KEY
});
// Same interface as FS factory methods
await storage.writeFile('documents/file.txt', 'content');
const content = await storage.readFile('documents/file.txt');
const exists = await storage.exists('documents/file.txt');Benefits of this approach:
Seamless Environment Switching
- Same code works across development, testing, and production
- No vendor lock-in - switch storage providers without code changes
- Easy A/B testing between different storage solutions
Developer Experience
- Three-line setup for any environment
- TypeScript-first with complete type safety
- Familiar fs/promises interface - no learning curve
Production Ready
- Stable interface used across all applications
- Reverse dependency makes testing trivial
- Zero configuration required for basic usage
Configuration Options
interface LinodeObjectStorageOptions {
region: string; // Linode region (e.g., 'us-southeast-1')
bucket: string; // Bucket name
accessKey: string; // Linode Object Storage access key
secretKey: string; // Linode Object Storage secret key
prefix?: string; // Optional path prefix
endpoint?: string; // Custom endpoint (optional)
}Complete API Reference
All implementations follow the same IAsyncFileSystem interface:
File Operations
// Write file
await fs.writeFile(path: string, content: string): Promise<void>
// Read file
await fs.readFile(path: string): Promise<string>
// Check if file exists
await fs.exists(path: string): Promise<boolean>
// Delete file
await fs.deleteFile(path: string): Promise<void>
await fs.unlink(path: string): Promise<void> // Alias
// Get file statistics
await fs.stat(path: string): Promise<FileStats>Directory Operations
// List directory contents
await fs.readDir(path: string): Promise<string[]>
// Create directory (no-op in object storage)
await fs.mkdir(path: string): Promise<void>
await fs.ensureDir(path: string): Promise<void> // Alias
// Remove directory and contents
await fs.rmdir(path: string): Promise<void>
await fs.deleteDir(path: string): Promise<void> // AliasFile Permissions
// Set file permissions (no-op in object storage)
await fs.chmod(path: string, mode: number): Promise<void>Real-World Examples
Testing with In-Memory Filesystem
import { FS } from '@nodules/linode-storage';
describe('DocumentService', () => {
const testFs = FS.async.memory();
const service = new DocumentService(testFs);
beforeEach(() => {
// In-memory filesystem is automatically clean for each test
});
it('should save and load documents', async () => {
await service.saveDocument('test-doc', 'content');
const content = await service.loadDocument('test-doc');
expect(content).toBe('content');
});
});Development with Local Filesystem
import { FS } from '@nodules/linode-storage';
// Development environment - files stored locally
const fs = FS.async.node();
const service = new DocumentService(fs);
// Files go to local filesystem for easy debugging
await service.saveDocument('debug-doc', 'debug content');
// Check ./documents/debug-doc.txt on your filesystemProduction with Linode Object Storage
import { FS } from '@nodules/linode-storage';
const fs = FS.async.linode({
region: 'us-southeast-1',
bucket: 'production-documents',
accessKey: process.env.LINODE_ACCESS_KEY,
secretKey: process.env.LINODE_SECRET_KEY,
prefix: 'app-v2/' // Namespace your application data
});
const service = new DocumentService(fs);
// Same code, now running on cloud storageError Handling
try {
const content = await fs.readFile('nonexistent.txt');
} catch (error) {
if (error.message.includes('not found')) {
console.log('File does not exist');
} else {
console.error('Storage error:', error.message);
}
}Testing
import { FS } from '@nodules/linode-storage';
// Testing is simple - use memory filesystem
const testFs = FS.async.memory();
const service = new DocumentService(testFs);
// No setup required, no cleanup needed
await service.saveDocument('test', 'content');
expect(await service.loadDocument('test')).toBe('content');TypeScript Support
Full TypeScript definitions included. No additional @types packages required.
interface FileStats {
size: number;
mtime: Date;
ctime: Date;
atime: Date;
mode: number;
isFile(): boolean;
isDirectory(): boolean;
isSymbolicLink(): boolean;
}Changelog
See CHANGELOG.md for version history and migration notes.
Support
- Issues: GitHub Issues
- Documentation: GitHub Wiki
- More examles See [nodules.io/docs/linode-storage]
- Contact us [email protected]
Enterprise Services
Professional support available through Nodules.io
- Enterprise licensing for commercial applications
- Priority support with SLA guarantees
- Security audits with every release
- Penetration testing and vulnerability assessment programs
- Dedicated technical account management and escalation procedures
- Comprehensive testing and validation reports
- Compliance validation for SOC 2, GDPR, HIPAA, and industry-specific regulations
- White-glove onboarding and integration support
- Private module development and customization
Contact: [email protected]
License
MIT License - see LICENSE for details.
Security: Report vulnerabilities to [email protected]
