Skip to content

Retrieve Your Data

This guide shows how to fetch a previously uploaded file from your chosen Main Storage Provider (MSP) using its deterministic file key. You’ll use the file key to download the file and stream the bytes directly to disk—avoiding loading the whole object into memory.

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 ts-node @types/node
      
      yarn add -D typescript ts-node @types/node
      
      npm install -D typescript 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
      
  • A file uploaded to DataHaven, along with the file key

Install Dependencies

pnpm add @storagehub-sdk/core @storagehub-sdk/msp-client @storagehub/types-bundle @polkadot/api @storagehub/api-augment viem
yarn add @storagehub-sdk/core @storagehub-sdk/msp-client @storagehub/types-bundle @polkadot/api @storagehub/api-augment viem
npm install @storagehub-sdk/core @storagehub-sdk/msp-client @storagehub/types-bundle @polkadot/api @storagehub/api-augment viem

Initialize Clients and Authenticate MSP Client

First, you'll need to set up the necessary clients to connect to the DataHaven network, which runs on a dual-protocol architecture (Substrate for core logic and EVM for compatibility).

If you've already followed the Upload a File guide, your clients may already be initialized and your MSP client authenticated. In that case, review the placeholders at the bottom of the following snippet to see where you'll add logic in this guide, then skip ahead to Download Your File from MSP Client.

Create an index.ts and add the following code:

Note

The code below uses DataHaven Testnet configuration values, which include the Chain ID, RPC URL, WSS URL, MSP URL, and token metadata. If you’re running a local devnet, make sure to replace these with your local configuration parameters. You can find all the relevant local devnet values in the Starter Kit.

index.ts
import '@storagehub/api-augment';
import { ApiPromise, WsProvider } from '@polkadot/api';
import { types } from '@storagehub/types-bundle';
import { HttpClientConfig, initWasm } from '@storagehub-sdk/core';
import {
  AuthStatus,
  DownloadResult,
  HealthStatus,
  MspClient,
} from '@storagehub-sdk/msp-client';
import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
import {
  Chain,
  WalletClient,
  createWalletClient,
  defineChain,
  http,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

async function run() {
  // For anything from @storagehub-sdk/core to work, initWasm() is required
  // on top of the file
  await initWasm();

  // --- viem setup ---
  // Define DataHaven chain, as expected by viem
  const chain: Chain = defineChain({
    id: 55931,
    name: 'DataHaven Testnet',
    nativeCurrency: { name: 'Mock', symbol: 'MOCK', decimals: 18 },
    rpcUrls: {
      default: { http: ['https://services.datahaven-testnet.network/testnet'] },
    },
  });

  // Define account from a private key
  const account = privateKeyToAccount('INSERT_PRIVATE_KEY' as `0x${string}`);
  const address = account.address;

  // Create a wallet client using the defined chain, account, and RPC URL
  const walletClient: WalletClient = createWalletClient({
    chain,
    account,
    transport: http('https://services.datahaven-testnet.network/testnet'),
  });

  // --- Polkadot.js API setup ---
  const provider = new WsProvider(
    'wss://services.datahaven-testnet.network/testnet'
  );
  const polkadotApi: ApiPromise = await ApiPromise.create({
    provider,
    typesBundle: types,
    noInitWarn: true,
  });

  // --- Connect to MSP Client and authenticate account ---
  const baseUrl = 'https://deo-dh-backend.testnet.datahaven-infra.network/';
  const httpConfig: HttpClientConfig = { baseUrl: baseUrl };
  // A temporary authentication token obtained after Sign-In with Ethereum (SIWE).
  // If not yet authenticated, this will remain undefined and the client will operate in read-only mode.
  // Authentication is required for file downloads.
  let sessionToken: string | undefined = undefined;
  const sessionProvider = async () =>
    sessionToken
      ? ({ token: sessionToken, user: { address: address } } as const)
      : undefined;
  const mspClient = await MspClient.connect(httpConfig, sessionProvider);

  // Check MSP Health Status
  const mspHealth: HealthStatus = await mspClient.info.getHealth();
  console.log('MSP service health:', mspHealth);

  // Trigger the SIWE (Sign-In with Ethereum) flow.
  // This prompts the connected wallet to sign an EIP-4361 message,
  // which the MSP backend verifies to issue a JWT session token
  const siweSession = await mspClient.auth.SIWE(walletClient);
  console.log('SIWE Session:', siweSession);
  // Store the obtained session token for future authenticated requests
  sessionToken = (siweSession as { token: string }).token;


  // --- Upload file logic ---
  // **PLACEHOLDER FOR STEP 1: DOWNLOAD FILE FROM MSP CLIENT**
  // **PLACEHOLDER FOR STEP 2: SAVE DOWNLOADED FILE**

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

await run();

Download Your File from MSP Client

Call the files.downloadFile function of the mspClient and pass the file key in hex format as follows:

// **PLACEHOLDER FOR STEP 1: DOWNLOAD FILE FROM MSP CLIENT**
const fileKey = 'INSERT_FILE_KEY_AS_HEX';

// Must be authenticated to download files
const downloadResponse: DownloadResult = await mspClient.files.downloadFile(
  fileKey
);

// Check if the download response was successful
if (downloadResponse.status !== 200) {
  throw new Error(`Download failed with status: ${downloadResponse.status}`);
}

Save Downloaded File

To save the retrieved file from the MSP on your local machine, add the following code:

// **PLACEHOLDER FOR STEP 2: SAVE DOWNLOADED FILE**
// Define the local path where the downloaded file will be saved
// Here it is resolved relative to the current module’s URL.
const downloadPath = new URL(
  '../../files/filename_downloaded.jpeg', // make sure the file extension matches the original file
  import.meta.url
).pathname;

// Create a writable stream to the target file path
// This stream will receive binary data chunks and write them to disk.
const writeStream = createWriteStream(downloadPath);

// Convert the Web ReadableStream into a Node.js-readable stream
const readableStream = Readable.fromWeb(downloadResponse.stream as any);

// Pipe the readable (input) stream into the writable (output) stream
// This transfers the file data chunk by chunk and closes the write stream automatically
// when finished.
readableStream.pipe(writeStream);

console.log('Downloaded file saved to:', downloadPath);
View complete script
index.ts
import '@storagehub/api-augment';
import { ApiPromise, WsProvider } from '@polkadot/api';
import { types } from '@storagehub/types-bundle';
import { HttpClientConfig, initWasm } from '@storagehub-sdk/core';
import {
  AuthStatus,
  DownloadResult,
  HealthStatus,
  MspClient,
} from '@storagehub-sdk/msp-client';
import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
import {
  Chain,
  WalletClient,
  createWalletClient,
  defineChain,
  http,
} from 'viem';
import { privateKeyToAccount } from 'viem/accounts';

async function run() {
  // For anything from @storagehub-sdk/core to work, initWasm() is required
  // on top of the file
  await initWasm();

  // --- viem setup ---
  // Define DataHaven chain, as expected by viem
  const chain: Chain = defineChain({
    id: 55931,
    name: 'DataHaven Testnet',
    nativeCurrency: { name: 'Mock', symbol: 'MOCK', decimals: 18 },
    rpcUrls: {
      default: { http: ['https://services.datahaven-testnet.network/testnet'] },
    },
  });

  // Define account from a private key
  const account = privateKeyToAccount('INSERT_PRIVATE_KEY' as `0x${string}`);
  const address = account.address;

  // Create a wallet client using the defined chain, account, and RPC URL
  const walletClient: WalletClient = createWalletClient({
    chain,
    account,
    transport: http('https://services.datahaven-testnet.network/testnet'),
  });

  // --- Polkadot.js API setup ---
  const provider = new WsProvider(
    'wss://services.datahaven-testnet.network/testnet'
  );
  const polkadotApi: ApiPromise = await ApiPromise.create({
    provider,
    typesBundle: types,
    noInitWarn: true,
  });

  // --- Connect to MSP Client and authenticate account ---
  const baseUrl = 'https://deo-dh-backend.testnet.datahaven-infra.network/';
  const httpConfig: HttpClientConfig = { baseUrl: baseUrl };
  // A temporary authentication token obtained after Sign-In with Ethereum (SIWE).
  // If not yet authenticated, this will remain undefined and the client will operate in read-only mode.
  // Authentication is required for file downloads.
  let sessionToken: string | undefined = undefined;
  const sessionProvider = async () =>
    sessionToken
      ? ({ token: sessionToken, user: { address: address } } as const)
      : undefined;
  const mspClient = await MspClient.connect(httpConfig, sessionProvider);

  // Check MSP Health Status
  const mspHealth: HealthStatus = await mspClient.info.getHealth();
  console.log('MSP service health:', mspHealth);

  // Trigger the SIWE (Sign-In with Ethereum) flow.
  // This prompts the connected wallet to sign an EIP-4361 message,
  // which the MSP backend verifies to issue a JWT session token
  const siweSession = await mspClient.auth.SIWE(walletClient);
  console.log('SIWE Session:', siweSession);
  // Store the obtained session token for future authenticated requests
  sessionToken = (siweSession as { token: string }).token;


  // --- Retrieve data logic ---
  const fileKey = 'INSERT_FILE_KEY_AS_HEX';

  // Must be authenticated to download files
  const downloadResponse: DownloadResult = await mspClient.files.downloadFile(
    fileKey
  );

  // Check if the download response was successful
  if (downloadResponse.status !== 200) {
    throw new Error(`Download failed with status: ${downloadResponse.status}`);
  }

  // Define the local path where the downloaded file will be saved
  // Here it is resolved relative to the current module’s URL.
  const downloadPath = new URL(
    '../../files/filename_downloaded.jpeg', // make sure the file extension matches the original file
    import.meta.url
  ).pathname;

  // Create a writable stream to the target file path
  // This stream will receive binary data chunks and write them to disk.
  const writeStream = createWriteStream(downloadPath);

  // Convert the Web ReadableStream into a Node.js-readable stream
  const readableStream = Readable.fromWeb(downloadResponse.stream as any);

  // Pipe the readable (input) stream into the writable (output) stream
  // This transfers the file data chunk by chunk and closes the write stream automatically
  // when finished.
  readableStream.pipe(writeStream);

  console.log('Downloaded file saved to:', downloadPath);

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

await run();

Next Steps

Last update: November 11, 2025
| Created: October 17, 2025