Smart contracts are not just static codeβthey emit events that can be listened to in real-time, enabling dApps to react immediately to on-chain activity. In a modern backend built with NestJS, listening to Ethereum events using Web3.js is a powerful way to automate workflows, trigger actions, or update state based on blockchain transactions.
In this guide, weβll walk you through how to:
- Connect to the Ethereum blockchain with Web3.js
- Subscribe to smart contract events
- Cleanly handle connections and disconnections
- Integrate the event listener into a NestJS service lifecycle
Letβs get started!
π¦ Prerequisitesβ
Before diving into the code, make sure you have:
- A NestJS project set up (
@nestjs/cli
) - An Ethereum WebSocket endpoint (e.g., Infura)
- Contract ABI and deployed contract address
Install the required package:
npm install web3
π Step-by-Step Implementationβ
1. Create the Web3Serviceβ
Here's a complete service example that connects to Ethereum via WebSocket and listens to a specific contract event:
// web3.service.ts
import { Injectable, OnModuleInit, OnModuleDestroy } from '@nestjs/common';
import Web3 from 'web3';
import { Subscription } from 'web3-core-subscriptions';
@Injectable()
export class Web3Service implements OnModuleInit, OnModuleDestroy {
private web3: Web3;
private contract: any;
private eventSubscription: Subscription<any>;
async onModuleInit() {
// Connect to Ethereum node using WebSocket
this.web3 = new Web3('wss://mainnet.infura.io/ws/v3/YOUR_INFURA_PROJECT_ID');
const abi = [ /* Contract ABI */ ];
const contractAddress = '0xYourContractAddress';
this.contract = new this.web3.eth.Contract(abi, contractAddress);
this.subscribeToEvents();
}
private subscribeToEvents() {
this.eventSubscription = this.contract.events.YourEventName({
fromBlock: 'latest',
})
.on('connected', (subscriptionId) => {
console.log(`Event listener connected: ${subscriptionId}`);
})
.on('data', (event) => {
console.log('Event received:', event.returnValues);
// TODO: Process event (e.g., save to DB, trigger business logic)
})
.on('error', (error) => {
console.error('Event listener error:', error);
// Optionally implement retry or fallback logic here
});
}
async onModuleDestroy() {
if (this.eventSubscription) {
this.eventSubscription.unsubscribe((error, success) => {
if (success) {
console.log('Event subscription successfully unsubscribed.');
} else {
console.error('Error unsubscribing:', error);
}
});
}
// Optional: close WebSocket connection
if (this.web3?.currentProvider && typeof this.web3.currentProvider.disconnect === 'function') {
(this.web3.currentProvider as any).disconnect(1000, 'App shutdown');
console.log('WebSocket disconnected.');
}
}
}
π§ Key Concepts Explainedβ
1. OnModuleInit
/ OnModuleDestroy
β
These lifecycle hooks ensure your event listener is started when the service is initialized and properly cleaned up when the app is shut down.
2. WebSocket Connectionβ
We're using wss://mainnet.infura.io/ws/v3/YOUR_INFURA_PROJECT_ID
to connect to Ethereum. WebSockets allow real-time event streaming.
β οΈ Always use WebSocket endpoints for subscriptions. HTTP JSON-RPC does not support event subscriptions.
3. Smart Contract & ABIβ
To listen to a smart contract event, you need:
- The ABI of the contract
- The deployed contract address
- The correct event name (case-sensitive)
4. Event Subscription Handlingβ
.on('data')
fires every time a new event is emitted..on('error')
is crucial to handle dropped connections or failures..unsubscribe()
ensures memory leaks and dangling connections are avoided.
β Use Cases for Event Listenersβ
Here are practical ways you can use Web3 event listeners in a NestJS app:
- Real-time dashboards: Update frontend UI based on blockchain activity
- Notifications: Send SMS/Email when specific events happen on-chain
- Analytics: Aggregate metrics like total mints, transfers, volume, etc.
- Automation: Trigger off-chain logic like payouts or access control
π§ͺ Testing Tipsβ
- Deploy the same contract to a testnet like Goerli or Sepolia
- Use Web3 console or Truffle/Hardhat to trigger events manually
- Confirm events using Etherscan or Web3 dev tools
π‘ Best Practicesβ
- Always check the connection status
- Use retry logic or exponential backoff in case of dropped connections
- Filter events by topics if listening to high-traffic contracts
- Secure your WebSocket URL (do not expose it in frontend code)
π§© Bonus: Extending the Serviceβ
Want to go further?
- Add a database service to persist event data (MongoDB, PostgreSQL, etc.)
- Add event queues (like BullMQ) to offload heavy post-processing
- Emit custom NestJS events to communicate across modules
π Conclusionβ
Listening to smart contract events in real-time is a foundational building block for any dApp backend or blockchain-aware server. With NestJS and Web3.js, you can build scalable, event-driven apps that respond instantly to on-chain activity.
By leveraging service lifecycle hooks, clean code practices, and proper Web3 subscriptions, you can stay ahead in the Web3 world with reliable, maintainable infrastructure.
Now go build the future of decentralized applications with confidence!