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.
Prerequisites
- Docker - Setup Instructions
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.
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.
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.
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).
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.
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
- MongoDB - Setup Instructions
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!