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

hetzner-storage-sdk

v1.0.2

Published

TypeScript SDK for Hetzner Storage Box, Storage Share, and Object Storage

Readme

Hetzner Storage SDK

A comprehensive TypeScript SDK for interacting with Hetzner Storage Box and Storage Share (Nextcloud).

Features

  • 🔧 Dual Provider Support: Works with both Storage Box and Storage Share
  • 📦 WebDAV Protocol: Reliable file operations via WebDAV
  • 🔒 Type-Safe: Full TypeScript support with complete type definitions
  • 🎯 Factory Pattern: Easy provider switching with unified interface
  • 📊 Management APIs: Full support for Robot API and Nextcloud OCS API
  • 🚀 Modern Async/Await: Promise-based API throughout

Installation

npm install hetzner-storage-sdk
# or
yarn add hetzner-storage-sdk

Quick Start

Storage Box (WebDAV)

import { HetznerStorageClient } from 'hetzner-storage-sdk';

const client = HetznerStorageClient.create({
  type: 'box',
  username: 'u123456',
  password: 'your-password',
  storageBoxId: '12345',
  robotUsername: 'robot-user', // Optional: for management operations
  robotPassword: 'robot-pass'
});

// List files
const files = await client.listFiles('/');

// Upload a file
await client.uploadFile('./local-file.txt', '/remote-file.txt');

// Download a file
await client.downloadFile('/remote-file.txt', './downloaded-file.txt');

// Create snapshot (requires Robot API credentials)
const snapshot = await client.createSnapshot('My backup');

Storage Share (Nextcloud)

import { HetznerStorageClient } from 'hetzner-storage-sdk';

const client = HetznerStorageClient.create({
  type: 'share',
  username: 'your-username',
  password: 'your-password',
  instance: 'u123456' // Your instance ID
});

// List files
const files = await client.listFiles('/');

// Upload a file
await client.uploadFile('./document.pdf', '/documents/document.pdf');

// Create a public share link
const share = await client.createPublicShare(
  '/documents/document.pdf',
  'optional-password',
  '2024-12-31', // Expiration date
  1 // Permissions (1 = read)
);

console.log('Share URL:', share.url);

Configuration

Storage Box Configuration

interface StorageBoxConfig {
  type: 'box';
  username: string;           // Storage Box username (e.g., 'u123456')
  password: string;           // Storage Box password
  storageBoxId?: string;      // Required for management operations
  robotUsername?: string;     // Robot API username
  robotPassword?: string;     // Robot API password
}

Storage Share Configuration

interface StorageShareConfig {
  type: 'share';
  username: string;  // Your Nextcloud username
  password: string;  // Your Nextcloud password
  instance: string;  // Instance ID (e.g., 'u123456')
}

API Reference

Common Methods (Both Providers)

File Operations

// List files in a directory
listFiles(remotePath?: string): Promise<FileMetadata[]>

// Upload a file
uploadFile(
  localPath: string | Buffer | Readable,
  remotePath: string,
  options?: UploadOptions
): Promise<void>

// Download a file
downloadFile(
  remotePath: string,
  localPath?: string,
  options?: DownloadOptions
): Promise<Buffer | void>

// Delete a file or directory
deleteFile(remotePath: string): Promise<void>

// Create a directory
createDirectory(remotePath: string): Promise<void>

// Move/rename a file
moveFile(fromPath: string, toPath: string): Promise<void>

// Copy a file
copyFile(fromPath: string, toPath: string): Promise<void>

// Check if file exists
exists(remotePath: string): Promise<boolean>

// Get file metadata
getMetadata(remotePath: string): Promise<FileMetadata>

// Close connections
disconnect(): Promise<void>

Storage Box Specific Methods

Robot API Management

// List all storage boxes
listStorageBoxes(): Promise<StorageBoxDetails[]>

// Get storage box details
getStorageBoxDetails(): Promise<StorageBoxDetails>

// Toggle services (SSH, Samba, WebDAV)
toggleServices(services: {
  webdav?: boolean;
  samba?: boolean;
  ssh?: boolean;
}): Promise<StorageBoxDetails>

// Snapshot management
createSnapshot(comment?: string): Promise<SnapshotInfo>
listSnapshots(): Promise<SnapshotInfo[]>
deleteSnapshot(snapshotName: string): Promise<void>
revertToSnapshot(snapshotName: string): Promise<void>

Storage Share Specific Methods

OCS API Share Management

// Create a public link share
createPublicShare(
  path: string,
  password?: string,
  expireDate?: string,
  permissions?: number
): Promise<OCSShareData>

// Create a user share
createUserShare(
  path: string,
  shareWith: string,
  permissions?: number
): Promise<OCSShareData>

// Create a group share
createGroupShare(
  path: string,
  shareWith: string,
  permissions?: number
): Promise<OCSShareData>

// List all shares
listShares(): Promise<OCSShareData[]>

// Get share details
getShare(shareId: string): Promise<OCSShareData>

// Update a share
updateShare(
  shareId: string,
  options: Partial<CreateShareOptions>
): Promise<OCSShareData>

// Delete a share
deleteShare(shareId: string): Promise<void>

// Get shares for a specific path
getSharesForPath(path: string): Promise<OCSShareData[]>

Usage Examples

Example 1: Backup with Snapshot (Storage Box)

import { HetznerStorageClient } from 'hetzner-storage-sdk';

async function createBackup() {
  const client = HetznerStorageClient.create({
    type: 'box',
    username: 'u123456',
    password: 'your-password',
    storageBoxId: '12345',
    robotUsername: 'robot-user',
    robotPassword: 'robot-pass'
  });

  // Upload backup files
  await client.createDirectory('/backups');
  await client.uploadFile('./database.sql', '/backups/database.sql');
  
  // Create snapshot
  const snapshot = await client.createSnapshot('Daily backup');
  console.log('Snapshot created:', snapshot.name);
  
  // List all snapshots
  const snapshots = await client.listSnapshots();
  console.log('Total snapshots:', snapshots.length);
  
  await client.disconnect();
}

Example 2: Share Files with Team (Storage Share)

import { HetznerStorageClient } from 'hetzner-storage-sdk';

async function shareWithTeam() {
  const client = HetznerStorageClient.create({
    type: 'share',
    username: 'your-username',
    password: 'your-password',
    instance: 'u123456'
  });

  // Upload document
  await client.uploadFile('./report.pdf', '/team/report.pdf');
  
  // Create public share with password
  const publicShare = await client.createPublicShare(
    '/team/report.pdf',
    'secure-password',
    '2024-12-31',
    1 // Read-only
  );
  
  console.log('Share link:', publicShare.url);
  console.log('Password:', 'secure-password');
  
  // Also share with specific team member
  const userShare = await client.createUserShare(
    '/team/report.pdf',
    'john.doe',
    31 // Full permissions
  );
  
  console.log('Shared with john.doe');
  
  await client.disconnect();
}

Example 3: Sync Local Directory (Storage Box)

import { HetznerStorageClient } from 'hetzner-storage-sdk';
import { readdir } from 'fs/promises';
import { join } from 'path';

async function syncDirectory(localDir: string, remoteDir: string) {
  const client = HetznerStorageClient.create({
    type: 'box',
    username: 'u123456',
    password: 'your-password'
  });

  // Create remote directory if it doesn't exist
  if (!(await client.exists(remoteDir))) {
    await client.createDirectory(remoteDir);
  }

  // Get local files
  const localFiles = await readdir(localDir);

  // Upload each file
  for (const file of localFiles) {
    const localPath = join(localDir, file);
    const remotePath = `${remoteDir}/${file}`;
    
    console.log(`Uploading ${file}...`);
    await client.uploadFile(localPath, remotePath, { overwrite: true });
  }

  console.log(`Synced ${localFiles.length} files`);
  await client.disconnect();
}

Example 4: Download and Process Files

import { HetznerStorageClient } from 'hetzner-storage-sdk';

async function processFiles() {
  const client = HetznerStorageClient.create({
    type: 'box',
    username: 'u123456',
    password: 'your-password'
  });

  // List all CSV files
  const files = await client.listFiles('/data');
  const csvFiles = files.filter(f => f.filename.endsWith('.csv'));

  for (const file of csvFiles) {
    // Download as buffer (not to disk)
    const buffer = await client.downloadFile(file.path) as Buffer;
    const content = buffer.toString('utf-8');
    
    // Process content
    console.log(`Processing ${file.filename}`);
    const lines = content.split('\n');
    console.log(`  - ${lines.length} lines`);
    
    // Your processing logic here...
  }

  await client.disconnect();
}

Example 5: Automated Backup Rotation

import { HetznerStorageClient } from 'hetzner-storage-sdk';

async function rotateBackups() {
  const client = HetznerStorageClient.create({
    type: 'box',
    username: 'u123456',
    password: 'your-password',
    storageBoxId: '12345',
    robotUsername: 'robot-user',
    robotPassword: 'robot-pass'
  });

  const backupDir = '/backups';
  const maxBackups = 7; // Keep last 7 backups

  // Create new backup
  const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
  const currentBackup = `${backupDir}/${timestamp}`;
  
  await client.createDirectory(currentBackup);
  await client.uploadFile('./data.tar.gz', `${currentBackup}/data.tar.gz`);
  
  // Create snapshot
  await client.createSnapshot(`Backup ${timestamp}`);

  // Get all backups
  const backups = await client.listFiles(backupDir);
  
  // Delete old backups
  if (backups.length > maxBackups) {
    const sortedBackups = backups.sort((a, b) => 
      a.lastModified.getTime() - b.lastModified.getTime()
    );
    
    const toDelete = sortedBackups.slice(0, backups.length - maxBackups);
    for (const backup of toDelete) {
      await client.deleteFile(backup.path);
      console.log(`Deleted old backup: ${backup.filename}`);
    }
  }

  await client.disconnect();
}

Error Handling

import { HetznerStorageClient, StorageError } from 'hetzner-storage-sdk';

async function handleErrors() {
  const client = HetznerStorageClient.create({
    type: 'box',
    username: 'u123456',
    password: 'your-password'
  });

  try {
    await client.uploadFile('./file.txt', '/remote/file.txt');
  } catch (error) {
    if (error instanceof Error) {
      const storageError = error as StorageError;
      console.error('Error:', storageError.message);
      console.error('Code:', storageError.code);
      console.error('Status:', storageError.statusCode);
    }
  } finally {
    await client.disconnect();
  }
}

Architecture

The SDK follows a clean architecture with:

  • Factory Pattern: HetznerStorageClient creates the appropriate provider
  • Provider Pattern: Abstract BaseStorageProvider with concrete implementations
  • Separation of Concerns:
    • File operations via WebDAV
    • Management via Robot API (Storage Box) or OCS API (Storage Share)
┌─────────────────────────────────┐
│   HetznerStorageClient          │
│   (Factory)                     │
└─────────────┬───────────────────┘
              │
      ┌───────┴────────┐
      │                │
┌─────▼─────┐    ┌────▼──────┐
│ Storage   │    │ Storage   │
│ Box       │    │ Share     │
│ Provider  │    │ Provider  │
└─────┬─────┘    └────┬──────┘
      │                │
  ┌───┴───┐        ┌───┴───┐
  │WebDAV │        │WebDAV │
  │Robot  │        │  OCS  │
  │  API  │        │  API  │
  └───────┘        └───────┘

Protocol Details

WebDAV Operations

Both Storage Box and Storage Share use WebDAV for file operations:

  • PROPFIND: List directory contents
  • PUT: Upload files
  • GET: Download files
  • DELETE: Remove files/directories
  • MKCOL: Create directories
  • MOVE: Move/rename files
  • COPY: Copy files

Storage Box URL Format

https://<username>.your-storagebox.de/

Storage Share URL Format

https://<instance>.your-storageshare.de/remote.php/dav/files/<username>/

TypeScript Support

The SDK is written in TypeScript and provides full type definitions:

import { 
  HetznerStorageClient,
  StorageBoxConfig,
  StorageShareConfig,
  FileMetadata,
  StorageBoxDetails,
  SnapshotInfo,
  OCSShareData
} from 'hetzner-storage-sdk';

// Full autocomplete and type checking
const client = HetznerStorageClient.create({
  type: 'box',
  username: 'u123456',
  password: 'pass'
});

const files: FileMetadata[] = await client.listFiles('/');

Dependencies

  • axios: HTTP client for API requests
  • webdav: WebDAV client for file operations

License

MIT

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Support

For issues or questions:

  • Check the examples in the documentation
  • Review the type definitions for detailed API information
  • Open an issue on the project repository
  • Contact Me