Skip to content

Manage Files and Buckets

This guide explains how to manage your storage resources on DataHaven using the StorageHub SDK. You will learn how to fetch all the buckets you have stored within a specific MSP, fetch all the files within a specific bucket in a specific MSP, request the removal of a file from the network, and delete buckets. It's important to periodically review and clean up unused data to avoid unnecessary costs, as buckets and files incur ongoing storage fees.

Prerequisites

  • Node.js v22+ installed
  • A TypeScript project

    Need a starter project?

    If you don't have an existing project, follow these steps to create a TypeScript project you can use to follow the guides in this section:

    1. Create a new project folder by executing the following command in the terminal:

      mkdir datahaven-project && cd datahaven-project
      
    2. Initialize a package.json file using the correct command for your package manager:

      pnpm init
      
      yarn init
      
      npm init --y
      
    3. Add the TypeScript and Node type definitions to your projects using the correct command for your package manager:

      pnpm add -D typescript tsx ts-node @types/node
      
      yarn add -D typescript tsx ts-node @types/node
      
      npm install -D typescript tsx ts-node @types/node
      
    4. Create a tsconfig.json file in the root of your project and paste the following configuration:

      tsconfig.json
      {
          "compilerOptions": {
              "target": "ES2022",
              "module": "nodenext",
              "moduleResolution": "NodeNext",
              "esModuleInterop": true,
              "strict": true,
              "skipLibCheck": true,
              "outDir": "dist",
              "declaration": true,
              "sourceMap": true
          },
          "include": ["src/**/*.ts"]
      }
      
    5. Initialize the src directory:

      mkdir src && touch src/index.ts
      
  • Dependencies installed

  • Clients initialized

  • A file uploaded to DataHaven, along with the file key

Initialize the Script Entry Point

First, create an index.ts file if you haven't already. Its run method will orchestrate all the logic in this guide, and you’ll replace the labelled placeholders with real code step by step. By now, your services folder (including the MSP and client helper services) should already be created. If not, see the Get Started guide.

The index.ts snippet below also imports bucketOperations.ts and fileOperations.ts, which are not in your project yet—that's expected, as you'll create them later in this guide.

Add the following code to your index.ts file:

index.ts
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
  getBucketFilesFromMSP,
  requestDeleteFile,
} from './operations/fileOperations.js';
import {
  deleteBucket,
  getBucketsFromMSP,
  waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';

async function run() {
  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // **PLACEHOLDER FOR STEP 1: AUTHENTICATE**
  // --- Data fetching logic ---
  // **PLACEHOLDER FOR STEP 2: GET YOUR BUCKETS**
  // **PLACEHOLDER FOR STEP 3: GET YOUR BUCKET FILES**
  // --- Data deleting logic ---
  // **PLACEHOLDER FOR STEP 4: REQUEST FILE DELETION**
  // **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
  // **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

Authenticate

Before any file operations, authenticate with the MSP. The authenticateUser helper signs a SIWE message and returns a session token that authorizes your uploads, updates, and deletions. Add the following code to use the authenticateUser helper method you've already implemented in mspService.ts:

index.ts // **PLACEHOLDER FOR STEP 1: AUTHENTICATE**
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
View complete index.ts up until this point
index.ts
  import '@storagehub/api-augment';
  import { initWasm } from '@storagehub-sdk/core';
  import { polkadotApi } from './services/clientService.js';
  import { authenticateUser } from './services/mspService.js';
  import {
    getBucketFilesFromMSP,
    requestDeleteFile,
  } from './operations/fileOperations.js';
  import {
    deleteBucket,
    getBucketsFromMSP,
    waitForBackendBucketEmpty,
  } from './operations/bucketOperations.js';

  async function run() {
  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // Authenticate
  const authProfile = await authenticateUser();
  console.log('Authenticated user profile:', authProfile);

  // --- Data fetching logic ---
  // **PLACEHOLDER FOR STEP 2: GET YOUR BUCKETS**
  // **PLACEHOLDER FOR STEP 3: GET YOUR BUCKET FILES**
  // --- Data deleting logic ---
  // **PLACEHOLDER FOR STEP 4: REQUEST FILE DELETION**
  // **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
  // **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

Get Buckets From MSP

To fetch your buckets stored in a specific MSP, create a helper method called getBucketsFromMSP in a separate bucketOperations.ts file and then update the index.ts file accordingly, to execute that logic.

Add Method to Get Buckets From MSP

  1. Create a new folder called operations within the src folder (at the same level as the services folder) like so:

    mkdir operations
    
  2. Create a new file within the operations folder called bucketOperations.ts.

  3. Add the following code:

    bucketOperations.ts
    import { Bucket, FileListResponse } from '@storagehub-sdk/msp-client';
    import { storageHubClient, publicClient } from '../services/clientService.js';
    import { mspClient } from '../services/mspService.js';
    
    export async function getBucketsFromMSP(): Promise<Bucket[]> {
      const buckets: Bucket[] = await mspClient.buckets.listBuckets();
      return buckets;
    }
    
    // Add other helper methods here
    

Call the Get Buckets From MSP Helper Method

Update index.ts with the following code to trigger the getBucketsFromMSP helper method you just implemented:

index.ts // **PLACEHOLDER FOR STEP 2: GET YOUR BUCKETS**
// Get buckets from MSP
const buckets = await getBucketsFromMSP();
console.log('Buckets in MSP:', buckets);
View complete index.ts up until this point
index.ts
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
  getBucketFilesFromMSP,
  requestDeleteFile,
} from './operations/fileOperations.js';
import {
  deleteBucket,
  getBucketsFromMSP,
  waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';

async function run() {
  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // Authenticate
  const authProfile = await authenticateUser();
  console.log('Authenticated user profile:', authProfile);

  // --- Data fetching logic ---

  // Get buckets from MSP
  const buckets = await getBucketsFromMSP();
  console.log('Buckets in MSP:', buckets);

  // **PLACEHOLDER FOR STEP 3: GET YOUR BUCKET FILES**
  // --- Data deleting logic ---
  // **PLACEHOLDER FOR STEP 4: REQUEST FILE DELETION**
  // **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
  // **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

If you run the script with the code above, the full response should look like this:

ts-node index.ts
Buckets in MSP: [
  {
    bucketId: '0xcfc81ed6cea71b005b3c4d08f15df08807e1545167a5e70c3b512fac3338ff87',
    name: 'bucket-003',
    root: '0x0000000000000000000000000000000000000000000000000000000000000000',
    isPublic: true,
    sizeBytes: 0,
    valuePropId: '0x0000000000000000000000000000000000000000000000000000000000000000',
    fileCount: 0
  },
  {
    bucketId: '0xdd2148ff63c15826ab42953a9d214770e6c2a73b22b83d28819a1777ab9d1322',
    name: 'bucket-004',
    root: '0x6b3de909a86212096a7d7b51253667e15f05170519c4c6029166af4e49378b23',
    isPublic: true,
    sizeBytes: 0,
    valuePropId: '0x0000000000000000000000000000000000000000000000000000000000000000',
    fileCount: 0
  },
  {
    bucketId: '0x354a938554bbae16379f9fcbd04fdee5050a0ec31763426eb341f76a30936a4b',
    name: 'bucket-005',
    root: '0x0000000000000000000000000000000000000000000000000000000000000000',
    isPublic: true,
    sizeBytes: 0,
    valuePropId: '0x0000000000000000000000000000000000000000000000000000000000000000',
    fileCount: 0
  }
]

Get Bucket Files From MSP

To fetch your files from a specific bucket stored in a specific MSP, create a helper method called getBucketFilesFromMSP in a separate fileOperations.ts file and then update the index.ts file accordingly, to execute that logic.

Add Method to Get Bucket Files From MSP

  1. Create a new file within the operations folder called fileOperations.ts.

  2. Add the following code:

fileOperations.ts
import { storageHubClient, publicClient } from '../services/clientService.js';
import { mspClient } from '../services/mspService.js';
import { FileInfo } from '@storagehub-sdk/core';
import { FileListResponse } from '@storagehub-sdk/msp-client';

export async function getBucketFilesFromMSP(
  bucketId: string,
): Promise<FileListResponse> {
  const files: FileListResponse = await mspClient.buckets.getFiles(bucketId);
  return files;
}

// Add other helper methods here

Call the Get Bucket Files from MSP Helper Method

Update index.ts with the following code to trigger the getBucketFilesFromMSP helper method you just implemented:

index.ts // **PLACEHOLDER FOR STEP 3: GET YOUR BUCKET FILES**
// Get bucket files from MSP
const files = await getBucketFilesFromMSP(bucketId);
console.log(`Files in bucket with ID ${bucketId}:`);
View complete index.ts up until this point
index.ts
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
  getBucketFilesFromMSP,
  requestDeleteFile,
} from './operations/fileOperations.js';
import {
  deleteBucket,
  getBucketsFromMSP,
  waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';

async function run() {
  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // Authenticate
  const authProfile = await authenticateUser();
  console.log('Authenticated user profile:', authProfile);

  // --- Data fetching logic ---

  // Get buckets from MSP
  const buckets = await getBucketsFromMSP();
  console.log('Buckets in MSP:', buckets);

  // Get bucket files from MSP
  const files = await getBucketFilesFromMSP(bucketId);
  console.log(`Files in bucket with ID ${bucketId}:`);

  // --- Data deleting logic ---
  // **PLACEHOLDER FOR STEP 4: REQUEST FILE DELETION**
  // **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
  // **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

If you run the script with the code above, the response should look like this:

ts-node index.ts
Files in bucket with ID 0x354a938554bbae16379f9fcbd04fdee5050a0ec31763426eb341f76a30936a4b:
{
  "bucketId": "0x354a938554bbae16379f9fcbd04fdee5050a0ec31763426eb341f76a30936a4b",
  "files": [
    {
      "name": "/",
      "children": [
        {
          "name": "helloworld.txt",
          "type": "file",
          "sizeBytes": 18,
          "fileKey": "0x03cd70aca1208002fc308dc8feebdeababb4deb46c053632e3d70adcec013d85",
          "status": "ready",
          "uploadedAt": "2026-01-14T14:21:00.910Z"
        }
      ]
    }
  ]
}

Request File Deletion

To request file deletion, create a helper method called requestDeleteFile in your fileOperations.ts file and then update the index.ts file accordingly, to execute that logic. First, fetch the file’s metadata from the MSP and then submit a deletion request using the StorageHub SDK.

It’s important to note that files are not removed instantly. When a deletion request succeeds, the file is marked for deletion on-chain, but both the MSP and all BSPs storing it still have it in their Merkle Patricia Forests until they pass the mandatory storage proof challenge. After that, the runtime automatically updates the Merkle Patricia Forest roots to remove the file.

Add Method to Request File Deletion

Add the following code:

fileOperations.ts
export async function requestDeleteFile(
  bucketId: string,
  fileKey: string,
): Promise<boolean> {
  // Get file info before deletion
  const fileInfo: FileInfo = await mspClient.files.getFileInfo(
    bucketId,
    fileKey,
  );
  console.log('File info:', fileInfo);

  // Request file deletion
  const txHashRequestDeleteFile: `0x${string}` =
    await storageHubClient.requestDeleteFile(fileInfo);
  console.log('requestDeleteFile() txHash:', txHashRequestDeleteFile);

  // Wait for delete file transaction receipt
  const receiptRequestDeleteFile = await publicClient.waitForTransactionReceipt(
    {
      hash: txHashRequestDeleteFile,
    },
  );
  console.log('requestDeleteFile() txReceipt:', receiptRequestDeleteFile);
  if (receiptRequestDeleteFile.status !== 'success') {
    throw new Error(`File deletion failed: ${txHashRequestDeleteFile}`);
  }

  console.log(
    `File with key ${fileKey} deleted successfully from bucket ${bucketId}`,
  );
  return true;
}

Call the Request File Deletion Helper Method

Update index.ts with the following code to trigger the requestDeleteFile helper method you just implemented:

index.ts // **PLACEHOLDER FOR STEP 4: REQUEST FILE DELETION**
// Request file deletion
const isDeletionRequestSuccessful = await requestDeleteFile(
  bucketId,
  fileKey,
);
console.log(
  'File deletion request submitted successfully:',
  isDeletionRequestSuccessful,
);
View complete index.ts up until this point
index.ts
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
  getBucketFilesFromMSP,
  requestDeleteFile,
} from './operations/fileOperations.js';
import {
  deleteBucket,
  getBucketsFromMSP,
  waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';

async function run() {
  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // Authenticate
  const authProfile = await authenticateUser();
  console.log('Authenticated user profile:', authProfile);

  // --- Data fetching logic ---

  // Get buckets from MSP
  const buckets = await getBucketsFromMSP();
  console.log('Buckets in MSP:', buckets);

  // Get bucket files from MSP
  const files = await getBucketFilesFromMSP(bucketId);
  console.log(`Files in bucket with ID ${bucketId}:`);

  // --- Data deleting logic ---

  // Request file deletion
  const isDeletionRequestSuccessful = await requestDeleteFile(
    bucketId,
    fileKey,
  );
  console.log(
    'File deletion request submitted successfully:',
    isDeletionRequestSuccessful,
  );

  // **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
  // **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

Note

After a file is uploaded to a Main Storage Provider (MSP), the network allows a 10-minute window for Backup Storage Providers (BSPs) to replicate the file to the required count. Within this time window, the deletion of a file cannot be requested. If the replication target is not met within this window, the request transitions to expired even though the upload to the MSP succeeded.

If you run the script with the code above, the full response should look like this:

ts-node index.ts
File info: {
 fileKey: '0x03cd70aca1208002fc308dc8feebdeababb4deb46c053632e3d70adcec013d85',
  fingerprint: '0x1bc3a71173c16c1eee04f7e7cf2591678b0b6cdf08eb81c638ae60a38b706aad',
  bucketId: '0x354a938554bbae16379f9fcbd04fdee5050a0ec31763426eb341f76a30936a4b',
  location: 'helloworld.txt',
  size: 18,
  isPublic: true,
  uploadedAt: 2025-11-05T14:19:55.911Z,
  status: 'ready'
}
requestDeleteFile() txHash: 0x5e33858fb2399ffbfaa90f9abb1bb6d06f978c5b25fe9d552a05ac6fd17f055c
requestDeleteFile() txReceipt: {
  transactionHash: '0x5e33858fb2399ffbfaa90f9abb1bb6d06f978c5b25fe9d552a05ac6fd17f055c',
  transactionIndex: 0,
  blockHash: '0xd3ad1ede25a2abb1673c26cac35c55ca3aaf6976db181582525838ff4b5b9498',
  from: '0x00fa35d84a43db75467d2b2c1ed8974aca57223e',
  to: '0x0000000000000000000000000000000000000404',
  blockNumber: 115472n,
  cumulativeGasUsed: 32400n,
  gasUsed: 32400n,
  contractAddress: null,
  logs: [
    {
      address: '0x0000000000000000000000000000000000000404',
      topics: [Array],
      data: '0x',
      blockHash: '0xd3ad1ede25a2abb1673c26cac35c55ca3aaf6976db181582525838ff4b5b9498',
      blockNumber: 115472n,
      transactionHash: '0x5e33858fb2399ffbfaa90f9abb1bb6d06f978c5b25fe9d552a05ac6fd17f055c',
      transactionIndex: 0,
      logIndex: 0,
      transactionLogIndex: '0x0',
      removed: false
    }
  ],
  logsBloom: '0x00000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000040000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000100000000004000000000200000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000000000000800000004000000080000000000000000000000000000000000000010000000000000000000000000000080000',
  status: 'success',
  effectiveGasPrice: 1000000000n,
  type: 'legacy'
}
File with key 0x03cd70aca1208002fc308dc8feebdeababb4deb46c053632e3d70adcec013d85 deleted successfully from bucket 0x354a938554bbae16379f9fcbd04fdee5050a0ec31763426eb341f76a30936a4b File deletion request submitted successfully: true

Wait for Backend to Return Empty Bucket

Right after your file's deletion is requested, your script will immediately try to delete the bucket your file was in. At this point, the file might not have been deleted, or if it has been, DataHaven’s indexer may not have processed that block yet. Until the indexer catches up, the MSP backend won't show up-to-date data, so any bucket deletion attempt will fail. To avoid that race condition, you’ll add a small polling helper that waits for the indexer to acknowledge your bucket is empty before continuing.

  1. Add the following code in your bucketOperations.ts file:

    bucketOperations.ts
    export async function waitForBackendBucketEmpty(bucketId: string) {
      const maxAttempts = 144; // 12 minutes total (144 * 5s)
      const delayMs = 5000;
    
      for (let i = 0; i < maxAttempts; i++) {
        try {
          const bucketFiles: FileListResponse =
            await mspClient.buckets.getFiles(bucketId);
    
          if (bucketFiles.files.length === 0) {
            console.log('Bucket is empty in MSP backend:', bucketFiles);
            return;
          }
          console.log(
            `Checking MSP backend for empty bucket... ${bucketFiles.files.length} file(s) remaining. ` +
              `Attempt ${i + 1}/${maxAttempts}`,
          );
        } catch (error: any) {
          if (error.status === 404 || error.body.error === 'Not found: Record') {
            console.log(`Bucket not found in MSP backend (404).`);
            throw new Error(`Bucket ${bucketId} not found in MSP backend`);
          } else {
            console.log('Unexpected error while fetching bucket from MSP:', error);
            throw error;
          }
        }
        await new Promise((r) => setTimeout(r, delayMs));
      }
      throw new Error(`Bucket ${bucketId} not empty in MSP backend after waiting`);
    }
    
View complete bucketOperations.ts up until this point
bucketOperations.ts
import { Bucket, FileListResponse } from '@storagehub-sdk/msp-client';
import { storageHubClient, publicClient } from '../services/clientService.js';
import { mspClient } from '../services/mspService.js';

export async function getBucketsFromMSP(): Promise<Bucket[]> {
  const buckets: Bucket[] = await mspClient.buckets.listBuckets();
  return buckets;
}

export async function waitForBackendBucketEmpty(bucketId: string) {
  const maxAttempts = 144; // 12 minutes total (144 * 5s)
  const delayMs = 5000;

  for (let i = 0; i < maxAttempts; i++) {
    try {
      const bucketFiles: FileListResponse =
        await mspClient.buckets.getFiles(bucketId);

      if (bucketFiles.files.length === 0) {
        console.log('Bucket is empty in MSP backend:', bucketFiles);
        return;
      }
      console.log(
        `Checking MSP backend for empty bucket... ${bucketFiles.files.length} file(s) remaining. ` +
          `Attempt ${i + 1}/${maxAttempts}`,
      );
    } catch (error: any) {
      if (error.status === 404 || error.body.error === 'Not found: Record') {
        console.log(`Bucket not found in MSP backend (404).`);
        throw new Error(`Bucket ${bucketId} not found in MSP backend`);
      } else {
        console.log('Unexpected error while fetching bucket from MSP:', error);
        throw error;
      }
    }
    await new Promise((r) => setTimeout(r, delayMs));
  }
  throw new Error(`Bucket ${bucketId} not empty in MSP backend after waiting`);
}

// Add other helper methods here
  1. Update the index.ts file to trigger the helper method you just implemented:

    index.ts // **PLACEHOLDER FOR STEP 5: WAIT FOR BACKEND TO RETURN EMPTY BUCKET**
    // Wait for backend to process deletion and verify bucket is empty
    await waitForBackendBucketEmpty(bucketId);
    

    The response should look something like this:

    ts-node index.ts
    Checking MSP backend for empty bucket... 1 file(s) remaining. Attempt 1/144
    Checking MSP backend for empty bucket... 1 file(s) remaining. Attempt 2/144
    Checking MSP backend for empty bucket... 1 file(s) remaining. Attempt 3/144
    Checking MSP backend for empty bucket... 1 file(s) remaining. Attempt 4/144
    Checking MSP backend for empty bucket... 1 file(s) remaining. Attempt 5/144
    Bucket is empty in MSP backend: { files: [] }
    /

View complete index.ts up until this point
index.ts
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
  getBucketFilesFromMSP,
  requestDeleteFile,
} from './operations/fileOperations.js';
import {
  deleteBucket,
  getBucketsFromMSP,
  waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';

async function run() {
  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // Authenticate
  const authProfile = await authenticateUser();
  console.log('Authenticated user profile:', authProfile);

  // --- Data fetching logic ---

  // Get buckets from MSP
  const buckets = await getBucketsFromMSP();
  console.log('Buckets in MSP:', buckets);

  // Get bucket files from MSP
  const files = await getBucketFilesFromMSP(bucketId);
  console.log(`Files in bucket with ID ${bucketId}:`);

  // --- Data deleting logic ---

  // Request file deletion
  const isDeletionRequestSuccessful = await requestDeleteFile(
    bucketId,
    fileKey,
  );
  console.log(
    'File deletion request submitted successfully:',
    isDeletionRequestSuccessful,
  );

  // Wait for backend to process deletion and verify bucket is empty
  await waitForBackendBucketEmpty(bucketId);

  // **PLACEHOLDER FOR STEP 6: DELETE A BUCKET**

  // Disconnect the Polkadot API at the very end
  await polkadotApi.disconnect();
}

await run();

Delete a Bucket

To delete a bucket, create a helper method called deleteBucket in your bucketOperations.ts file and then update the index.ts file accordingly, to execute that logic.

Note

A bucket can only be deleted if all its files have already been deleted. Use the mspClient.buckets.getFiles() method by passing a bucketId as a parameter to check all the files currently stored in that bucket.

Add Method to Delete Bucket

Create a new file within the operations folder called bucketOperations.ts and add the following code:

bucketOperations.ts
export async function deleteBucket(bucketId: string): Promise<boolean> {
  const txHash: `0x${string}` | undefined = await storageHubClient.deleteBucket(
    bucketId as `0x${string}`,
  );
  console.log('deleteBucket() txHash:', txHash);
  if (!txHash) {
    throw new Error('deleteBucket() did not return a transaction hash');
  }

  // Wait for transaction
  const receipt = await publicClient.waitForTransactionReceipt({
    hash: txHash,
  });
  console.log('deleteBucket() txReceipt:', receipt);
  if (receipt.status !== 'success') {
    throw new Error(`Bucket deletion failed: ${txHash}`);
  }

  return true;
}

Call the Delete Bucket Helper Method

Update index.ts with the following code to trigger the deleteBucket helper method you just implemented:

index.ts // **PLACEHOLDER FOR STEP 6: DELETE BUCKET**
// Delete bucket
const isBucketDeletionSuccessful = await deleteBucket(bucketId);
console.log('Bucket deletion successful:', isBucketDeletionSuccessful);

If you run the script with the bucket deletion code, the response should include:

ts-node index.ts
deleteBucket() txHash: 0x30204c4e27430ad81067e3dcbb283e46dac3a8a2ca484c980277056658128f13
deleteBucket() txReceipt: {
  transactionHash: '0x30204c4e27430ad81067e3dcbb283e46dac3a8a2ca484c980277056658128f13',
  transactionIndex: 0,
  blockHash: '0x2784726df3ea7f5333749c54f9c62e1813404a5475e0d040a456194c2636d2d2',
  from: '0x00fa35d84a43db75467d2b2c1ed8974aca57223e',
  to: '0x0000000000000000000000000000000000000404',
  blockNumber: 117400n,
  cumulativeGasUsed: 228432n,
  gasUsed: 228432n,
  contractAddress: null,
  logs: [
    {
      address: '0x0000000000000000000000000000000000000404',
      topics: [Array],
      data: '0x',
      blockHash: '0x2784726df3ea7f5333749c54f9c62e1813404a5475e0d040a456194c2636d2d2',
      blockNumber: 117400n,
      transactionHash: '0x30204c4e27430ad81067e3dcbb283e46dac3a8a2ca484c980277056658128f13',
      transactionIndex: 0,
      logIndex: 0,
      transactionLogIndex: '0x0',
      removed: false
    }
  ],
  logsBloom: '0x00000000000000040000000000000000000000000000000000000000000000000000000000000000000000000008000008000000000010000000000000000000000000040000000000020000000000000000000000000100000000000000000000040000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000010000800000000000000000000000080000',
  status: 'success',
  effectiveGasPrice: 1000000000n,
  type: 'legacy'
}
Bucket deletion successful: true
View complete index.ts
index.ts
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import {
  getBucketFilesFromMSP,
  requestDeleteFile,
} from './operations/fileOperations.js';
import {
  deleteBucket,
  getBucketsFromMSP,
  waitForBackendBucketEmpty,
} from './operations/bucketOperations.js';

async function run() {
  // Initialize WASM
  await initWasm();

  const bucketId = 'INSERT_BUCKET_ID'; // `0x${string}`
  const fileKey = 'INSERT_FILE_KEY'; // `0x${string}`
  // If not in hex already, convert it with .toHex()

  // Authenticate
  const authProfile = await authenticateUser();
  console.log('Authenticated user profile:', authProfile);

  // Get buckets from MSP
  const buckets = await getBucketsFromMSP();
  console.log('Buckets in MSP:', buckets);

  // Get bucket files from MSP
  const files = await getBucketFilesFromMSP(bucketId);
  console.log(`Files in bucket with ID ${bucketId}:`);

  // Request file deletion
  const isDeletionRequestSuccessful = await requestDeleteFile(
    bucketId,
    fileKey,
  );
  console.log(
    'File deletion request submitted successfully:',
    isDeletionRequestSuccessful,
  );

  // Wait for backend to process deletion and verify bucket is empty
  await waitForBackendBucketEmpty(bucketId);

  // Delete bucket
  const isBucketDeletionSuccessful = await deleteBucket(bucketId);
  console.log('Bucket deletion successful:', isBucketDeletionSuccessful);

  await polkadotApi.disconnect();
}

run();
View complete bucketOperations.ts
bucketOperations.ts
import { Bucket, FileListResponse } from '@storagehub-sdk/msp-client';
import { storageHubClient, publicClient } from '../services/clientService.js';
import { mspClient } from '../services/mspService.js';

export async function getBucketsFromMSP(): Promise<Bucket[]> {
  const buckets: Bucket[] = await mspClient.buckets.listBuckets();
  return buckets;
}

export async function waitForBackendBucketEmpty(bucketId: string) {
  const maxAttempts = 144; // 12 minutes total (144 * 5s)
  const delayMs = 5000;

  for (let i = 0; i < maxAttempts; i++) {
    try {
      const bucketFiles: FileListResponse =
        await mspClient.buckets.getFiles(bucketId);

      if (bucketFiles.files.length === 0) {
        console.log('Bucket is empty in MSP backend:', bucketFiles);
        return;
      }
      console.log(
        `Checking MSP backend for empty bucket... ${bucketFiles.files.length} file(s) remaining. ` +
          `Attempt ${i + 1}/${maxAttempts}`,
      );
    } catch (error: any) {
      if (error.status === 404 || error.body.error === 'Not found: Record') {
        console.log(`Bucket not found in MSP backend (404).`);
        throw new Error(`Bucket ${bucketId} not found in MSP backend`);
      } else {
        console.log('Unexpected error while fetching bucket from MSP:', error);
        throw error;
      }
    }
    await new Promise((r) => setTimeout(r, delayMs));
  }
  throw new Error(`Bucket ${bucketId} not empty in MSP backend after waiting`);
}

export async function deleteBucket(bucketId: string): Promise<boolean> {
  const txHash: `0x${string}` | undefined = await storageHubClient.deleteBucket(
    bucketId as `0x${string}`,
  );
  console.log('deleteBucket() txHash:', txHash);
  if (!txHash) {
    throw new Error('deleteBucket() did not return a transaction hash');
  }

  // Wait for transaction
  const receipt = await publicClient.waitForTransactionReceipt({
    hash: txHash,
  });
  console.log('deleteBucket() txReceipt:', receipt);
  if (receipt.status !== 'success') {
    throw new Error(`Bucket deletion failed: ${txHash}`);
  }

  return true;
}
View complete fileOperations.ts
fileOperations.ts
import { storageHubClient, publicClient } from '../services/clientService.js';
import { mspClient } from '../services/mspService.js';
import { FileInfo } from '@storagehub-sdk/core';
import { FileListResponse } from '@storagehub-sdk/msp-client';

export async function getBucketFilesFromMSP(
  bucketId: string,
): Promise<FileListResponse> {
  const files: FileListResponse = await mspClient.buckets.getFiles(bucketId);
  return files;
}

export async function requestDeleteFile(
  bucketId: string,
  fileKey: string,
): Promise<boolean> {
  // Get file info before deletion
  const fileInfo: FileInfo = await mspClient.files.getFileInfo(
    bucketId,
    fileKey,
  );
  console.log('File info:', fileInfo);

  // Request file deletion
  const txHashRequestDeleteFile: `0x${string}` =
    await storageHubClient.requestDeleteFile(fileInfo);
  console.log('requestDeleteFile() txHash:', txHashRequestDeleteFile);

  // Wait for delete file transaction receipt
  const receiptRequestDeleteFile = await publicClient.waitForTransactionReceipt(
    {
      hash: txHashRequestDeleteFile,
    },
  );
  console.log('requestDeleteFile() txReceipt:', receiptRequestDeleteFile);
  if (receiptRequestDeleteFile.status !== 'success') {
    throw new Error(`File deletion failed: ${txHashRequestDeleteFile}`);
  }

  console.log(
    `File with key ${fileKey} deleted successfully from bucket ${bucketId}`,
  );
  return true;
}

Next Steps

Last update: February 4, 2026
| Created: November 7, 2025