skip to content
logo
Table of Contents

Building a Blockchain Node with JavaScript

Blockchain technology continues to evolve beyond cryptocurrency applications into enterprise solutions. This guide demonstrates how to build a functioning blockchain node using Node.js, providing you with fundamental knowledge of distributed ledger architecture.


Blockchain Fundamentals

Before diving into code, let’s understand key blockchain components:

  • Block structure: Contains transactions, timestamp, and cryptographic hash
  • Consensus mechanism: How nodes agree on the valid state
  • Peer-to-peer networking: How nodes communicate
  • Cryptographic validation: Ensuring data integrity

Setting Up the Project

First, create a new Node.js project and install dependencies:

Terminal window
mkdir blockchain-node
cd blockchain-node
npm init -y
npm install crypto-js express body-parser ws uuid

Creating the Block Structure

Let’s define our block structure with TypeScript:

import SHA256 from 'crypto-js/sha256';
interface Transaction {
sender: string;
recipient: string;
amount: number;
timestamp: number;
}
class Block {
index: number;
timestamp: number;
transactions: Transaction[];
previousHash: string;
hash: string;
nonce: number;
constructor(
index: number,
timestamp: number,
transactions: Transaction[],
previousHash: string = ''
) {
this.index = index;
this.timestamp = timestamp;
this.transactions = transactions;
this.previousHash = previousHash;
this.nonce = 0;
this.hash = this.calculateHash();
}
calculateHash(): string {
return SHA256(
this.index +
this.previousHash +
this.timestamp +
JSON.stringify(this.transactions) +
this.nonce
).toString();
}
mineBlock(difficulty: number): void {
const target = Array(difficulty + 1).join('0');
while (this.hash.substring(0, difficulty) !== target) {
this.nonce++;
this.hash = this.calculateHash();
}
console.log(`Block mined: ${this.hash}`);
}
}

Implementing the Blockchain

Now, let’s create the blockchain class to manage blocks:

class Blockchain {
chain: Block[];
difficulty: number;
pendingTransactions: Transaction[];
miningReward: number;
constructor() {
this.chain = [this.createGenesisBlock()];
this.difficulty = 4;
this.pendingTransactions = [];
this.miningReward = 100;
}
createGenesisBlock(): Block {
return new Block(0, Date.now(), [], '0');
}
getLatestBlock(): Block {
return this.chain[this.chain.length - 1];
}
minePendingTransactions(miningRewardAddress: string): void {
const rewardTx: Transaction = {
sender: 'SYSTEM',
recipient: miningRewardAddress,
amount: this.miningReward,
timestamp: Date.now()
};
this.pendingTransactions.push(rewardTx);
const block = new Block(
this.chain.length,
Date.now(),
this.pendingTransactions,
this.getLatestBlock().hash
);
block.mineBlock(this.difficulty);
console.log('Block successfully mined!');
this.chain.push(block);
this.pendingTransactions = [];
}
addTransaction(transaction: Transaction): void {
// Validate transaction
if (!transaction.sender || !transaction.recipient || transaction.amount <= 0) {
throw new Error('Invalid transaction');
}
// Add to pending transactions
this.pendingTransactions.push(transaction);
}
getBalanceOfAddress(address: string): number {
let balance = 0;
for (const block of this.chain) {
for (const trans of block.transactions) {
if (trans.sender === address) {
balance -= trans.amount;
}
if (trans.recipient === address) {
balance += trans.amount;
}
}
}
return balance;
}
isChainValid(): boolean {
for (let i = 1; i < this.chain.length; i++) {
const currentBlock = this.chain[i];
const previousBlock = this.chain[i - 1];
if (currentBlock.hash !== currentBlock.calculateHash()) {
return false;
}
if (currentBlock.previousHash !== previousBlock.hash) {
return false;
}
}
return true;
}
}

Building the P2P Network

Next, let’s create a peer-to-peer network to synchronize blockchain data across nodes:

import WebSocket from 'ws';
import { v4 as uuidv4 } from 'uuid';
interface PeerMessage {
type: string;
data: any;
}
class P2PServer {
private blockchain: Blockchain;
private sockets: WebSocket[];
private nodeId: string;
constructor(blockchain: Blockchain) {
this.blockchain = blockchain;
this.sockets = [];
this.nodeId = uuidv4();
}
listen(port: number): void {
const server = new WebSocket.Server({ port });
server.on('connection', (socket) => this.connectSocket(socket));
console.log(`Listening for peer connections on port: ${port}`);
}
connectToPeers(newPeers: string[]): void {
newPeers.forEach((peer) => {
const socket = new WebSocket(peer);
socket.on('open', () => this.connectSocket(socket));
socket.on('error', () => console.log('Connection failed'));
});
}
connectSocket(socket: WebSocket): void {
this.sockets.push(socket);
console.log('Socket connected');
this.messageHandler(socket);
this.sendChain(socket);
}
messageHandler(socket: WebSocket): void {
socket.on('message', (data) => {
const message: PeerMessage = JSON.parse(data.toString());
switch (message.type) {
case 'CHAIN':
this.handleChainMessage(message.data);
break;
case 'TRANSACTION':
this.handleTransactionMessage(message.data);
break;
}
});
}
sendChain(socket: WebSocket): void {
socket.send(JSON.stringify({
type: 'CHAIN',
data: this.blockchain.chain
}));
}
syncChains(): void {
this.sockets.forEach(socket => this.sendChain(socket));
}
broadcastTransaction(transaction: Transaction): void {
this.sockets.forEach(socket => {
socket.send(JSON.stringify({
type: 'TRANSACTION',
data: transaction
}));
});
}
handleChainMessage(chain: Block[]): void {
const receivedChain = chain.map(block => {
const reconstructedBlock = new Block(
block.index,
block.timestamp,
block.transactions,
block.previousHash
);
reconstructedBlock.hash = block.hash;
reconstructedBlock.nonce = block.nonce;
return reconstructedBlock;
});
if (receivedChain.length > this.blockchain.chain.length) {
console.log('Received chain is longer than current chain. Replacing chain.');
this.blockchain.chain = receivedChain;
}
}
handleTransactionMessage(transaction: Transaction): void {
this.blockchain.addTransaction(transaction);
}
}

Creating a REST API

Now, let’s create an API to interact with our blockchain:

import express from 'express';
import bodyParser from 'body-parser';
const app = express();
app.use(bodyParser.json());
const blockchain = new Blockchain();
const p2pServer = new P2PServer(blockchain);
// Get the entire blockchain
app.get('/blocks', (req, res) => {
res.json(blockchain.chain);
});
// Mine a new block
app.post('/mine', (req, res) => {
const { minerAddress } = req.body;
if (!minerAddress) {
return res.status(400).json({ error: 'Miner address is required' });
}
blockchain.minePendingTransactions(minerAddress);
p2pServer.syncChains();
res.redirect('/blocks');
});
// Create a new transaction
app.post('/transaction', (req, res) => {
const { sender, recipient, amount } = req.body;
if (!sender || !recipient || !amount) {
return res.status(400).json({ error: 'Invalid transaction data' });
}
const transaction: Transaction = {
sender,
recipient,
amount,
timestamp: Date.now()
};
blockchain.addTransaction(transaction);
p2pServer.broadcastTransaction(transaction);
res.json({ success: true, transaction });
});
// Get account balance
app.get('/balance/:address', (req, res) => {
const balance = blockchain.getBalanceOfAddress(req.params.address);
res.json({ address: req.params.address, balance });
});
// Connect to peers
app.post('/peers', (req, res) => {
const { peers } = req.body;
if (!peers || !Array.isArray(peers)) {
return res.status(400).json({ error: 'Invalid peers data' });
}
p2pServer.connectToPeers(peers);
res.json({ success: true });
});
const HTTP_PORT = process.env.HTTP_PORT || 3001;
app.listen(HTTP_PORT, () => {
console.log(`HTTP server listening on port ${HTTP_PORT}`);
});
const P2P_PORT = process.env.P2P_PORT || 5001;
p2pServer.listen(P2P_PORT);

Running Multiple Nodes

To test a distributed network, you can run multiple nodes on your local machine:

Terminal window
# Terminal 1: Start the first node
HTTP_PORT=3001 P2P_PORT=5001 npm start
# Terminal 2: Start the second node
HTTP_PORT=3002 P2P_PORT=5002 PEERS=ws://localhost:5001 npm start
# Terminal 3: Start the third node
HTTP_PORT=3003 P2P_PORT=5003 PEERS=ws://localhost:5001,ws://localhost:5002 npm start

Implementing Proof of Work

Our blockchain already includes a simple proof-of-work mechanism through the mineBlock method. This ensures that creating new blocks requires computational effort, making it difficult for attackers to manipulate the chain.

By adjusting the difficulty parameter, you can control how much work is needed to mine a block.


Security Considerations

This implementation is for educational purposes. For production systems, consider:

  1. Wallet security: Implement proper public/private key infrastructure
  2. Data validation: Add comprehensive transaction validation
  3. Network security: Add peer authentication
  4. Scalability solutions: Implement smart chain pruning and optimization
  5. Consensus improvements: Explore PoS or other efficient consensus mechanisms

Conclusion

You now have a functional blockchain node in JavaScript that demonstrates the core concepts of blockchain technology. You can continue to enhance this implementation by adding:

  • Digital signatures for transaction authentication
  • More advanced consensus algorithms
  • Smart contract functionality
  • Chain pruning mechanisms
  • UTXO or account-based transaction models

Blockchain technology continues to evolve rapidly, with new approaches to scaling, privacy, and interoperability emerging in 2025. By understanding the fundamentals presented here, you’ll be better positioned to work with more complex blockchain systems in production environments.