> ## Documentation Index
> Fetch the complete documentation index at: https://docs.peaq.xyz/llms.txt
> Use this file to discover all available pages before exploring further.

# MongoDB

> Store documents off chain in MongoDB and write their hashes to peaq storage. Compare document hashes with on-chain values to verify data integrity.

MongoDB is an open-source NoSQL database that stores JSON-like BSON documents. It suits large or loosely structured payloads that do not belong on chain.

peaq storage holds a 64-byte key and a 256-byte value, so anything larger lives in your own storage. For how the two layers divide the work, see [On-chain vs Off-chain](/peaqchain/build/basic-operations/on-chain-vs-off-chain).

<img src="https://mintcdn.com/peaq/bfUMfJGiNN2sOYvn/assets/img/off-chain-storage-1.png?fit=max&auto=format&n=bfUMfJGiNN2sOYvn&q=85&s=686196dea854bf78b76bd333ecb320e8" alt="off-chain-storage-1" width="2772" height="1542" data-path="assets/img/off-chain-storage-1.png" />

## Prerequisites

* Node.js and npm installed.
* TypeScript and Node.js type declarations installed in your project:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
npm install --save-dev typescript @types/node
```

* Atlas CLI for the Atlas setup in step 4. With Homebrew on macOS or Linux:

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
brew install mongodb-atlas-cli
```

For other operating systems, follow the [Atlas CLI installation instructions](https://www.mongodb.com/docs/atlas/cli/current/install-atlas-cli/).

## Integration tutorial

Store the document in MongoDB and write its hash to peaq storage, keyed by the MongoDB document ID. Later you fetch the document from MongoDB, hash it again, and compare against the on-chain value to prove it was not altered.

### 1. Install the required packages

```bash theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
npm install mongodb @polkadot/api

```

### 2. Create a tsconfig.json file in your project directory

```json theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
{
  "compilerOptions": {
    "target": "es2018",
    "module": "commonjs",
    "strict": true,
    "esModuleInterop": true,
    "types": ["node"]
  }
}
```

### 3. Create a file named app.ts

```typescript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
import { MongoClient, Db, Collection, ObjectId } from 'mongodb';

class MongoDBStorage {
  private client: MongoClient;
  private db!: Db;
  private collection!: Collection;
  private collectionName: string;
  private dbName: string;

  constructor() {
    const connectionString = 'mongodb://localhost:27017';
    this.client = new MongoClient(connectionString);
    this.dbName = 'yourDataBaseName';
    this.collectionName = 'yourCollectionName';
  }

  async connect() {
    try {
      await this.client.connect();
      console.log('Connected successfully to MongoDB server');
      this.db = this.client.db(this.dbName);
      this.collection = this.db.collection(this.collectionName);
    } catch (error) {
      console.error('Error connecting to MongoDB:', error);
      throw error;
    }
  }

  async disconnect() {
    try {
      await this.client.close();
      console.log('Disconnected successfully from MongoDB server');
    } catch (error) {
      console.error('Error disconnecting from MongoDB:', error);
      throw error;
    }
  }

  async insertDocument(document: any) {
    try {
      const result = await this.collection.insertOne(document);
      console.log('Document inserted:', result);
      return result;
    } catch (error) {
      console.error('Error inserting document:', error);
      throw error;
    }
  }

  async findDocumentById(id: string) {
    const result = await this.collection.findOne({ _id: new ObjectId(id) });
    console.log('Found document by ID:', result);
    return result;
  }

  async updateDocument(id: string, update: any) {
    const result = await this.collection.updateOne({ _id: new ObjectId(id) }, { $set: update });
    console.log('Updated document:', result.modifiedCount);
    return result.modifiedCount > 0;
  }

  async deleteDocument(id: string) {
    const result = await this.collection.deleteOne({ _id: new ObjectId(id) });
    console.log('Deleted document:', result.deletedCount);
    return result.deletedCount > 0;
  }

}

export {MongoDBStorage}
```

### 4. Set up MongoDB

You can run MongoDB locally, in the cloud with Atlas, or serverless.

* Download [MongoDB](https://www.mongodb.com/try/download/community).
* Run `atlas setup` to create and configure an Atlas account. It prints your cluster name, cloud provider location, and admin username and password.
* For Atlas, replace `mongodb://localhost:27017` in the step 3 code with your cluster's `mongodb+srv://<username>:<password>@<cluster-host>/` connection string (`atlas setup` prints the credentials; the Atlas UI shows the host). For a local MongoDB, keep `mongodb://localhost:27017`. The [MongoDB documentation](https://www.mongodb.com/docs/manual/reference/connection-string) covers the format.
* Run `npx tsc` to compile the TypeScript, then rename the generated `app.js` to `app.cjs`. The next step imports the class from that file.

### 5. Connect to peaq

* Create a file named `peaq.js` in the same directory.
* Add `"type": "module"` to package.json.
* Paste the code below to open the peaq connection.

```javascript theme={"theme":{"light":"github-light-default","dark":"github-dark"}}
import { ApiPromise, WsProvider } from '@polkadot/api';
import { MongoDBStorage } from './app.cjs';
import { u8aToHex, u8aToU8a, u8aConcat } from "@polkadot/util";
import Keyring from "@polkadot/keyring";
import { decodeAddress, blake2AsHex } from '@polkadot/util-crypto';

// agung testnet. Swap in a peaq mainnet endpoint when you go live.
const WSS_URL = 'wss://wss-async-agung.peaq.xyz';
const OWNER_SEED = 'your funded account mnemonic';

const provider = new WsProvider(WSS_URL);
const api = await ApiPromise.create({ provider });

// creates a storage key to read data from chain
function createStorageKey(address, itemType) {
    const storageKeyByteArray = [];
    const decodedAddress = decodeAddress(address, false, 42);
    storageKeyByteArray.push(decodedAddress);

    const hashItemType = u8aToU8a(itemType);
    storageKeyByteArray.push(hashItemType);

    const key = u8aConcat(...storageKeyByteArray);

    const storageKey = blake2AsHex(key, 256);
    return storageKey
  }

// Store Big data in MongoDB, hash data on peaq Network, read to confirm data is the same
const main = async () => {
    // Data to store in MongoDB
    const data = "Hello World";

    // Store data in MongoDB
    const mongoDBStorage = new MongoDBStorage();
    await mongoDBStorage.connect();
    const insertedDocument = await mongoDBStorage.insertDocument({text: data});
    await mongoDBStorage.disconnect();

    // Hash data to store on chain
    const mongoID = insertedDocument.insertedId.toString();
    const storeHashData = blake2AsHex(data, 256);

    // Create wallet from seed of a user funded wallet
    const keyring = new Keyring({ type: 'sr25519' });
    const owner = keyring.addFromUri(OWNER_SEED);

    // Add to MongoDB id and hashed data to peaq storage
    await api.tx.peaqStorage.addItem(mongoID, storeHashData).signAndSend(owner);

    await sleep(30000); // Pause before reading; this delay does not guarantee inclusion.

    // Retrieve data from off-chain storage based on the id
    await mongoDBStorage.connect();
    const foundDocument = await mongoDBStorage.findDocumentById(mongoID);
    await mongoDBStorage.disconnect();

    const readId = foundDocument._id.toString();

    // Create a storage key and read hashed on-chain data storage
    const storageKey = createStorageKey(owner.address, readId);
    var onChainHashData = await api.query.peaqStorage.itemStore(storageKey);

    // Confirm the on-chain hash matches a fresh hash of the MongoDB document
    onChainHashData = onChainHashData.toHex();
    const hashData = blake2AsHex(foundDocument.text, 256);
    console.log(onChainHashData == hashData);

    await api.disconnect();
  }

  function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
  }

main();
```

`signAndSend` without a status callback returns a transaction hash before inclusion. The fixed delay above is only a pause before reading. To wait for inclusion, use a `signAndSend` status callback, check `dispatchError`, and wait for `status.isInBlock` (or `status.isFinalized` for finality) before reading on-chain storage.

The script uses the `MongoDBStorage` class from step 3. The document goes into MongoDB, and its hash goes into peaq storage as the pair `MongoDB_Id: Hashed_Data`. Reading the document back and re-hashing it shows whether it still matches the on-chain record.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.