Building Resilient Apps with ResilientDB

 

The ResilientDB Fullstack makes it easy to build and interact with blockchain-powered applications. At its core, it uses ResilientDB for high-speed transaction processing and ResVault as a wallet i...

The ResilientDB Fullstack makes it easy to build and interact with blockchain-powered applications. At its core, it uses ResilientDB for high-speed transaction processing and ResVault as a wallet interface for seamless interactions. Web apps can connect effortlessly using the ResVault web SDK, while developers also get SDK support for Python, Rust, and TypeScript, making it simple to integrate ResilientDB into different applications. Whether you’re building for the web or other platforms, ResilientDB Fullstack provides the tools to get started quickly and efficiently.

Core Components of a Resilient App

  • Your Decentralized App (dApp) – Built on ResilientDB to enable trustless, high-throughput transactions.
  • ResVault – A secure wallet interface that connects users to your dApp via the ResVault web SDK.
  • ResilientDB GraphQL Server – Provides an intuitive API layer for interacting with blockchain data.
  • ResilientDB KV/Smart Contract Service – A robust key-value store and smart contract execution engine, combined with Crow HTTP Server and SDK support for Python, TypeScript, and Rust.

ResilientDB Fullstack

Prerequisites

Setting Up ResilientDB in Minutes

Getting started with ResilientDB is incredibly simple with ResilientDB-Ansible, which automates the setup of the Crow HTTP server and GraphQL API. A cross-platform GUI installer with Tauri is also in the works to make deployment even easier.

Quick Setup Instructions

  • Clone the ResilientDB-Ansible repository:
    git clone https://github.com/ResilientEcosystem/resilientdb-ansible
    cd resilientdb-ansible
    
  • Build the Docker image:
    docker build -t resilientdb-ansible .
    
  • Run the container with required privileges:
    docker run --privileged -v /sys/fs/cgroup:/sys/fs/cgroup:ro -p 80:80 -p 18000:18000 -p 8000:8000 resilientdb-ansible
    

Once the container is running, you can access:

  • ResilientDB Crow HTTP Server at http://localhost/crow (Exposed port 18000)
  • ResilientDB GraphQL API at http://localhost/graphql (Exposed port 8000)

This setup automatically configures ResilientDB with 4 replicas and 1 client, with Nginx handling routing inside the container.

Set Up ResVault: Your Gateway to ResilientDB

ResVault is a Chrome extension wallet for ResilientDB, allowing users to commit and retrieve data, manage accounts, and track transactions, all through the ResilientDB GraphQL server. Think of it as Metamask for ResilientDB, but tailored for high-throughput blockchain applications.

Build ResVault from Source

Prerequisite

You need Node.js v16.20.2 for the build. The recommended way to manage multiple Node.js versions is using nvm.

Clone the ResVault Repository

git clone https://github.com/apache/incubator-resilientdb-resvault
cd incubator-resilientdb-resvault

Install Dependencies

npm install

Build the Wallet

npm run build

Verify Successful Build

If everything goes well, you’ll see this message:

The build folder is ready to be deployed.
You may serve it with a static server:

  serve -s build

Find out more about deployment here:

  https://cra.link/deployment

The wallet build will be available in the build directory inside incubator-resilientdb-resvault

Now, you’re all set to use ResVault as your secure wallet for ResilientDB transactions!

Add build to chrome

Once you have generated the build:

  • Open chrome and navigate to chrome://extensions/

  • Make sure developer mode is enabled using the toggle button.

  • Finally, load the extension by clicking on load unpacked button and then select the build directory that was created in the previous step.

  • Now you can open the wallet from the extension button and start using it!

Integrating ResVault SDK with Your Resilient App

If you’re looking for the simplest way to build a ResilientDB-powered React or Vue application with ResVault SDK integration, create-resilient-app is the tool for you!

Why Use create-resilient-app?

  • Scaffold a new ResilientDB app with React or Vue in seconds.
  • Supports both JavaScript and TypeScript for flexibility.
  • Automatically integrates ResVault SDK, so you can start interacting with ResilientDB right away.
  • No manual setup, just run a single command!

Installation & Setup

You don’t need to install anything globally. Just run:

npx create-resilient-app

Running the command without any flags will launch an interactive prompt.

You’ll be asked:

  • Project Name – Give your app a name.
  • Framework – Choose between React or Vue.
  • Language – Choose between JavaScript or TypeScript.

One-Command Setup

Skip the prompts by specifying options directly:

npx create-resilient-app --name my-app --framework react --language typescript

Example Commands

Create a React app with TypeScript:

npx create-resilient-app --name my-react-app --framework react --language typescript

Create a Vue app with JavaScript:

npx create-resilient-app --name my-vue-app --framework vue --language javascript

Project Setup & Running

After the project is generated, navigate to your project directory:

cd my-app

Then install the required dependencies:

npm install

Start Your Project

For React:

npm start

For Vue:

npm run dev

Now, you can customize your app and start building powerful ResilientDB-powered decentralized applications.

Resilient App

Next Steps: Connect Your Resilient App to ResVault

Now that you’ve set up your first Resilient App, it’s time to connect it to ResVault and start interacting with ResilientDB.

Open Your Resilient App

  • Navigate to your Resilient App in your browser.

Open ResVault & Create an Account

  • Open the ResVault extension in your browser.

  • If you haven’t created an account yet, set a secure password and let ResVault generate your keys securely.

ResVault SignUp

Choose a Network in ResVault and Connect

  • Once at the ResVault Dashboard, click the dropdown menu at the top.

  • By default, it is set to ResilientDB Mainnet, which connects to ResilientDB Cloud (cloud.resilientdb.com/graphql).

  • If you are running ResilientDB locally, select ResilientDB Localnet from the dropdown.

  • On the right side of the dropdown, you’ll see a site icon, click on it once to connect ResVault to your Resilient App.

ResVault connect

Click “Sign In Via ResVault” in Your Resilient App

  • Inside your Resilient App, click Sign In Via ResVault.

  • ResVault will prompt you to approve the transaction.

  • Once approved, ResVault will sign the transaction using your keys and send it to ResilientDB.

  • Your app will receive a callback confirming successful authentication and will be redirected to the authenticated page.

  • Now, you can send more transactions by submitting the form inside the authenticated page (with any JSON data as well).

ResVault authenticate

Submit Transactions from the Authenticated Page

  • Now that you’re authenticated, you can send more transactions using the form inside the authenticated page.

  • The form allows you to submit any JSON data, which will be securely signed and sent to ResilientDB.

ResVault authenticated page

You’re now fully connected to ResilientDB via ResVault! Whether on Mainnet (Cloud) or Localnet, you can authenticate, send transactions, and interact with decentralized applications seamlessly.

Client-Side Indexing Support in ResilientDB for Filtering in Your Resilient App

When building ResilientDB-powered applications, efficient data retrieval is crucial. If your Resilient App needs a filtering functionality, you can index ResilientDB transaction data on the client side using MongoDB. This tutorial will guide you through setting up real-time synchronization with MongoDB using ResilientDB’s WebSocket and HTTP APIs for efficient querying.

Prerequisites

Why Client-Side Indexing?

  • Efficient filtering of ResilientDB transactions for dApps.

  • Real-time sync using WebSocket and HTTP.

  • Query transactions based on public keys, timestamps, asset JSON, or metadata.

  • Automatic reconnection, batching, and concurrency handling.

Syncing ResilientDB Data to MongoDB for Efficient Filtering

Install the Required Library

For Node.js, install:

npm install resilient-node-cache

For Python, install:

pip install resilient-python-cache

Configure MongoDB and ResilientDB

Before syncing, ensure MongoDB is running and configure your connection settings:

  • Node.js Configuration (sync.js)
const { WebSocketMongoSync } = require('resilient-node-cache');

const mongoConfig = {
  uri: 'mongodb://localhost:27017',
  dbName: 'myDatabase',
  collectionName: 'myCollection',
};

/* resilientdb://localhost/crow to sync localnet */
const resilientDBConfig = {
  baseUrl: 'resilientdb://crow.resilientdb.com',
  httpSecure: true,
  wsSecure: true,
};

const sync = new WebSocketMongoSync(mongoConfig, resilientDBConfig);

sync.on('connected', () => {
  console.log('WebSocket connected.');
});

sync.on('data', (newBlocks) => {
  console.log('Received new blocks:', newBlocks);
});

sync.on('error', (error) => {
  console.error('Error:', error);
});

sync.on('closed', () => {
  console.log('Connection closed.');
});

(async () => {
  try {
    await sync.initialize();
    console.log('Synchronization initialized.');
  } catch (error) {
    console.error('Error during sync initialization:', error);
  }
})();
  • Python Configuration (sync.py)
import asyncio
from resilient_python_cache import ResilientPythonCache, MongoConfig, ResilientDBConfig

async def main():
    mongo_config = MongoConfig(
        uri="mongodb://localhost:27017",
        db_name="myDatabase",
        collection_name="myCollection"
    )

    # resilientdb://localhost/crow to sync localnet
    resilient_db_config = ResilientDBConfig(
        base_url="resilientdb://crow.resilientdb.com",
        http_secure=True,
        ws_secure=True
    )

    cache = ResilientPythonCache(mongo_config, resilient_db_config)

    cache.on("connected", lambda: print("WebSocket connected."))
    cache.on("data", lambda new_blocks: print("Received new blocks:", new_blocks))
    cache.on("error", lambda error: print("Error:", error))
    cache.on("closed", lambda: print("Connection closed."))

    try:
        await cache.initialize()
        print("Synchronization initialized.")
        await asyncio.Future()  # Run indefinitely
    except Exception as error:
        print("Error during sync initialization:", error)
    finally:
        await cache.close()

if __name__ == "__main__":
    asyncio.run(main())

Fetch Transactions by Public Key

Once transactions are synced to MongoDB, you can query them efficiently using the public key of the owner.

  • Node.js Query (fetchTransactions.js)
const { MongoClient } = require('mongodb');

const mongoConfig = {
  uri: 'mongodb://localhost:27017',
  dbName: 'myDatabase',
  collectionName: 'myCollection',
};

const targetPublicKey = "8LUKr81SmkdDhuBNAHfH9C8G5m6Cye2mpUggVu61USbD";

(async () => {
  const client = new MongoClient(mongoConfig.uri);

  try {
    await client.connect();
    const db = client.db(mongoConfig.dbName);
    const collection = db.collection(mongoConfig.collectionName);

    console.log('Connected to MongoDB for fetching transactions.');

    // Create an index for faster querying
    const indexName = await collection.createIndex({ "transactions.value.inputs.owners_before": 1 });
    console.log(`Index created: ${indexName}`);

    const pipeline = [
      { $unwind: "$transactions" },
      { $unwind: "$transactions.value.inputs" },
      { $match: { "transactions.value.inputs.owners_before": targetPublicKey } },
      { $sort: { "transactions.value.asset.data.timestamp": -1 } },
      { $project: { transaction: "$transactions", _id: 0 } }
    ];

    const transactions = await collection.aggregate(pipeline).toArray();
    console.log(`Transactions:`, JSON.stringify(transactions, null, 2));

  } catch (error) {
    console.error('Error fetching transactions:', error);
  } finally {
    await client.close();
  }
})();
  • Python Query (fetch_transactions.py)
from pymongo import MongoClient

mongo_config = {
    "uri": "mongodb://localhost:27017",
    "db_name": "myDatabase",
    "collection_name": "myCollection",
}

target_public_key = "8LUKr81SmkdDhuBNAHfH9C8G5m6Cye2mpUggVu61USbD"

client = MongoClient(mongo_config["uri"])
db = client[mongo_config["db_name"]]
collection = db[mongo_config["collection_name"]]

print("Connected to MongoDB for fetching transactions.")

# Create an index for optimized querying
index_name = collection.create_index("transactions.value.inputs.owners_before")
print(f"Index created: {index_name}")

# Define aggregation pipeline to fetch transactions by public key
pipeline = [
    {"$unwind": "$transactions"},
    {"$unwind": "$transactions.value.inputs"},
    {"$match": {"transactions.value.inputs.owners_before": target_public_key}},
    {"$sort": {"transactions.value.asset.data.timestamp": -1}},
    {"$project": {"transaction": "$transactions", "_id": 0}}
]

transactions = list(collection.aggregate(pipeline))

if transactions:
    print("Transactions:", transactions)
else:
    print(f"No transactions found for publicKey: {target_public_key}")

client.close()

Now, your Resilient App can efficiently filter transactions based on user-specific data without slow queries to the blockchain.

Using ResilientDB GraphQL for Transactions and Queries

ResilientDB provides a GraphQL API for seamless interaction with the blockchain, allowing you to send transactions and fetch transaction details efficiently.

Accessing ResilientDB GraphQL

  • For Localnet: ResilientDB GraphQL is available at:
    http://localhost/graphql
    
  • For Mainnet (ResilientDB Cloud):
    https://cloud.resilientdb.com/graphql
    

ResVault uses this GraphQL API internally to send transactions using stored keys. You can also use it directly in your application.


Sending a Transaction via GraphQL

You can send a transaction to ResilientDB using the postTransaction mutation.

Mutation Example

mutation {
  postTransaction(data: {
    operation: "CREATE"
    amount: 50632
    signerPublicKey: "8fPAqJvAFAkqGs8GdmDDrkHyR7hHsscVjes39TVVfN54"
    signerPrivateKey: "5R4ER6smR6c6fsWt3unPqP6Rhjepbn82Us7hoSj5ZYCc"
    recipientPublicKey: "ECJksQuF9UWi3DPCYvQqJPjF6BqSbXrnDiXUjdiVvkyH"
    asset: {
      data: {
        time: 1690881023169
      }
    }
  }) {
    id
  }
}

Explanation:

  • operation: "CREATE" defines the type of transaction.
  • amount: 50632 specifies the transaction value.
  • signerPublicKey & signerPrivateKey: Used for transaction signing.
  • recipientPublicKey: Defines the recipient of the transaction.
  • asset: Stores additional transaction data (any JSON).

🔹 Expected Response:

{
  "data": {
    "postTransaction": {
      "id": "f1df753f782dccd7e345175f67a3f8026113b1074724b202f24aa9073644ab47"
    }
  }
}

The response includes the transaction ID, which can be used to fetch details later.

Fetching a Transaction by ID

You can retrieve transaction details using the getTransaction query.

Query Example

query {
  getTransaction(id: "f1df753f782dccd7e345175f67a3f8026113b1074724b202f24aa9073644ab47") {
    id
    version
    amount
    metadata
    operation
    asset
    publicKey
    uri
    type
    signerPublicKey
  }
}

Explanation:

  • id: Transaction ID (retrieved from the previous mutation).

Expected Response:

{
  "data": {
    "getTransaction": {
      "id": "f1df753f782dccd7e345175f67a3f8026113b1074724b202f24aa9073644ab47",
      "version": "2.0",
      "amount": 50632,
      "metadata": null,
      "operation": "CREATE",
      "asset": {
        "data": {
          "time": 1690881023169
        }
      },
      "publicKey": "ECJksQuF9UWi3DPCYvQqJPjF6BqSbXrnDiXUjdiVvkyH",
      "uri": "ni:///sha-256;ORrcs4QnlphyiTL69-rcLNPNV_SfaCgmg7ywRhGgYBM?fpt=ed25519-sha-256&cost=131072",
      "type": "ed25519-sha-256",
      "signerPublicKey": "8fPAqJvAFAkqGs8GdmDDrkHyR7hHsscVjes39TVVfN54"
    }
  }
}

Note: Two transactions will have identical IDs if all the fields in the postTransaction mutation are exactly the same. This is because ResilientDB deterministically generates transaction IDs based on the input fields, ensuring consistency across identical transactions.

What You Can Do with ResilientDB GraphQL

  • Send transactions using postTransaction.

  • Retrieve transactions by ID using getTransaction.

  • Use it in ResVault for transaction signing.

  • Directly interact with ResilientDB via GraphQL instead of manually handling HTTP requests.

Congratulations! Your first Resilient App now has all the essential components set up! You’re now ready to build, authenticate, and interact seamlessly with ResilientDB. Happy coding!


Demo video

Coming soon!