Manage Files and Buckets¶
This guide explains how to manage your storage resources on DataHaven using the StorageHub SDK. You will learn how to request the removal of a file from the network and how to 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:
-
Create a new project folder by executing the following command in the terminal:
-
Initialize a
package.jsonfile using the correct command for your package manager: -
Add the TypeScript and Node type definitions to your projects using the correct command for your package manager:
-
Create a
tsconfig.jsonfile in the root of your project and paste the following configuration: -
Initialize the
srcdirectory:
-
-
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:
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import { requestDeleteFile } from './operations/fileOperations.js';
import { deleteBucket } 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()
// --- Data deletion logic ---
// **PLACEHOLDER FOR STEP 1: AUTHENTICATE**
// **PLACEHOLDER FOR STEP 2: REQUEST FILE DELETION**
// **PLACEHOLDER FOR STEP 3: 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:
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
View complete index.ts up until this point
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import { requestDeleteFile } from './operations/fileOperations.js';
import { deleteBucket } 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()
// --- Data deletion logic ---
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
// **PLACEHOLDER FOR STEP 2: REQUEST FILE DELETION**
// **PLACEHOLDER FOR STEP 3: DELETE A BUCKET**
// Disconnect the Polkadot API at the very end
await polkadotApi.disconnect();
}
await run();
Request File Deletion¶
To request file deletion, create a helper method called requestDeleteFile in a separate fileOperations.ts file and then update the index.ts file accordingly, in order 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 that file still have the file inside their Merkle Patricia Forests until they pass the mandatory storage proof challenge. After that, the runtime automatically updates their Merkle Patricia Forest roots to remove the file.
Add Method to Request File Deletion¶
-
Create a new folder called
operationswithin thesrcfolder (at the same level as theservicesfolder) like so: -
Create a new file within the
operationsfolder calledfileOperations.ts. -
Add the following code:
fileOperations.tsimport { storageHubClient, publicClient } from '../services/clientService.js'; import { mspClient } from '../services/mspService.js'; import { FileInfo } from '@storagehub-sdk/core'; 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:
// 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
import '@storagehub/api-augment';
import { initWasm } from '@storagehub-sdk/core';
import { polkadotApi } from './services/clientService.js';
import { authenticateUser } from './services/mspService.js';
import { requestDeleteFile } from './operations/fileOperations.js';
import { deleteBucket } 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()
// --- Data deletion logic ---
// Authenticate
const authProfile = await authenticateUser();
console.log('Authenticated user profile:', authProfile);
// Request file deletion
const isDeletionRequestSuccessful = await requestDeleteFile(
bucketId,
fileKey
);
console.log(
'File deletion request submitted successfully:',
isDeletionRequestSuccessful
);
// **PLACEHOLDER FOR STEP 3: 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:
File info: {
fileKey: '0xe0d46d3e57ab2aabd09aee9cf8425910de5028bc82a1a8c52774b7c71d6c8933',
fingerprint: '0x1bc3a71173c16c1eee04f7e7cf2591678b0b6cdf08eb81c638ae60a38b706aad',
bucketId: '0x5811b0cfc1529286c59988e55e3e7d701ba2d68bdebdafb9b891f0c887611108',
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 0xe0d46d3e57ab2aabd09aee9cf8425910de5028bc82a1a8c52774b7c71d6c8933 deleted successfully from bucket 0x5811b0cfc1529286c59988e55e3e7d701ba2d68bdebdafb9b891f0c887611108
File deletion request submitted successfully: true
Delete a Bucket¶
To delete a bucket, create a helper method called deleteBucket in a separate bucketOperations.ts file and then update the index.ts file accordingly, in order 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:
import { storageHubClient, publicClient } from '../services/clientService.js';
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:
const IsBucketDeletionSuccessful = await deleteBucket(bucketId);
console.log('Bucket deletion successful:', IsBucketDeletionSuccessful);
View complete 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 { requestDeleteFile } from './operations/fileOperations.js';
import { deleteBucket } 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);
// Request file deletion
const isDeletionRequestSuccessful = await requestDeleteFile(
bucketId,
fileKey
);
console.log(
'File deletion request submitted successfully:',
isDeletionRequestSuccessful
);
const IsBucketDeletionSuccessful = await deleteBucket(bucketId);
console.log('Bucket deletion successful:', IsBucketDeletionSuccessful);
await polkadotApi.disconnect();
}
run();
If you run the script with the bucket deletion code, the response should include:
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
Next Steps¶
| Created: November 7, 2025