求基于Node.js API连接React前端的Hyperledger Fabric 2.2示例
Hey there! I totally get your frustration—Hyperledger Fabric 2.x introduced some key changes from 1.4, so older examples often don’t translate cleanly. Let me walk you through a practical setup that connects your Fabric 2.2 network to a React frontend using the Node.js SDK v2.x.
Key Prerequisites First
Make sure you have these in place:
- Node.js v14+ (compatible with Fabric 2.2 SDK)
- A running Hyperledger Fabric 2.2 network (with channel, committed chaincode, and enrolled user identities)
- A React project (use
npx create-react-app fabric-react-frontendif you don’t have one)
Step 1: Build a Node.js Middleware (Critical!)
You can’t connect React directly to Fabric from the browser—browsers don’t support the necessary crypto libraries. Instead, create a Node.js backend to handle Fabric interactions, then let React call this backend via APIs.
Setup Backend Project
Initialize a new Node project:
mkdir fabric-backend && cd fabric-backend npm init -y npm install fabric-network fabric-ca-client express corsAdd your Fabric network’s
connection.json(generated when you set up your network) to the project root.Create the core Fabric connection logic (
server.js):const { Gateway, Wallets } = require('fabric-network'); const path = require('path'); const fs = require('fs'); const express = require('express'); const cors = require('cors'); const app = express(); app.use(cors()); app.use(express.json()); // Helper: Connect to Fabric network and get chaincode contract async function getContract() { // Load network configuration const ccpPath = path.resolve(__dirname, 'connection.json'); const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); // Initialize file system wallet (stores user identities) const walletPath = path.join(process.cwd(), 'wallet'); const wallet = await Wallets.newFileSystemWallet(walletPath); // Verify user identity exists (you'll need to enroll users first) const identity = await wallet.get('appUser'); if (!identity) { throw new Error('User "appUser" not found in wallet—enroll them first!'); } // Create gateway connection const gateway = new Gateway(); await gateway.connect(ccp, { wallet, identity: 'appUser', discovery: { enabled: true, asLocalhost: true } // Use false for remote networks }); // Get channel and chaincode contract const network = await gateway.getNetwork('mychannel'); const contract = network.getContract('mychaincode'); return { contract, gateway }; } // API: Query all assets from chaincode app.get('/api/assets', async (req, res) => { try { const { contract, gateway } = await getContract(); const result = await contract.evaluateTransaction('queryAllAssets'); await gateway.disconnect(); res.json(JSON.parse(result.toString())); } catch (err) { res.status(500).json({ error: err.message }); } }); // API: Create a new asset via chaincode transaction app.post('/api/assets', async (req, res) => { const { id, value } = req.body; try { const { contract, gateway } = await getContract(); await contract.submitTransaction('createAsset', id, value); await gateway.disconnect(); res.json({ message: 'Asset created successfully' }); } catch (err) { res.status(500).json({ error: err.message }); } }); const PORT = 5000; app.listen(PORT, () => console.log(`Backend running on http://localhost:${PORT}`));
Important: Enroll a User to the Wallet
Before running the backend, you need to enroll a user and store their identity in the wallet. Add a quick script (enrollUser.js) to do this:
const { Wallets } = require('fabric-network'); const FabricCAServices = require('fabric-ca-client'); const path = require('path'); const fs = require('fs'); async function enrollUser() { const ccpPath = path.resolve(__dirname, 'connection.json'); const ccp = JSON.parse(fs.readFileSync(ccpPath, 'utf8')); // Create CA client const caURL = ccp.certificateAuthorities['ca.org1.example.com'].url; const ca = new FabricCAServices(caURL); // Initialize wallet const walletPath = path.join(process.cwd(), 'wallet'); const wallet = await Wallets.newFileSystemWallet(walletPath); // Check if user already exists const userExists = await wallet.get('appUser'); if (userExists) { console.log('User "appUser" already exists in wallet'); return; } // Enroll admin first (you should have admin credentials in your wallet) const adminIdentity = await wallet.get('admin'); if (!adminIdentity) { console.log('Admin identity not found—enroll admin first!'); return; } // Get admin signer const provider = wallet.getProviderRegistry().getProvider(adminIdentity.type); const adminSigner = await provider.getSigner(adminIdentity); // Register and enroll appUser const secret = await ca.register({ affiliation: 'org1.department1', enrollmentID: 'appUser', role: 'client' }, adminSigner); const enrollment = await ca.enroll({ enrollmentID: 'appUser', enrollmentSecret: secret }); const x509Identity = { credentials: { certificate: enrollment.certificate, privateKey: enrollment.key.toBytes(), }, mspId: 'Org1MSP', type: 'X.509', }; await wallet.put('appUser', x509Identity); console.log('Successfully enrolled user "appUser" and stored in wallet'); } enrollUser().catch(err => { console.error('Error enrolling user:', err); process.exit(1); });
Run this script once: node enrollUser.js
Step 2: Connect React Frontend to Backend
In your React project, install axios for API calls:
npm install axios
Update src/App.js to interact with the backend:
import { useState, useEffect } from 'react'; import axios from 'axios'; function App() { const [assets, setAssets] = useState([]); const [newAssetId, setNewAssetId] = useState(''); const [newAssetValue, setNewAssetValue] = useState(''); const [error, setError] = useState(''); // Fetch assets on component mount useEffect(() => { fetchAssets(); }, []); const fetchAssets = async () => { try { const response = await axios.get('http://localhost:5000/api/assets'); setAssets(response.data); setError(''); } catch (err) { setError('Failed to load assets: ' + err.message); } }; const createAsset = async (e) => { e.preventDefault(); try { await axios.post('http://localhost:5000/api/assets', { id: newAssetId, value: newAssetValue }); setNewAssetId(''); setNewAssetValue(''); fetchAssets(); // Refresh assets list setError(''); } catch (err) { setError('Failed to create asset: ' + err.message); } }; return ( <div style={{ maxWidth: 800, margin: '0 auto', padding: '2rem' }}> <h1>Fabric 2.2 Asset Manager</h1> {error && <p style={{ color: 'red' }}>{error}</p>} <form onSubmit={createAsset} style={{ marginBottom: '2rem' }}> <input type="text" placeholder="Asset ID" value={newAssetId} onChange={(e) => setNewAssetId(e.target.value)} required style={{ marginRight: '1rem', padding: '0.5rem' }} /> <input type="text" placeholder="Asset Value" value={newAssetValue} onChange={(e) => setNewAssetValue(e.target.value)} required style={{ marginRight: '1rem', padding: '0.5rem' }} /> <button type="submit" style={{ padding: '0.5rem 1rem' }}>Create Asset</button> </form> <h2>Existing Assets</h2> <ul style={{ listStyle: 'none', padding: 0 }}> {assets.map(asset => ( <li key={asset.ID} style={{ padding: '0.5rem', borderBottom: '1px solid #eee' }}> <strong>{asset.ID}</strong>: {asset.Value} </li> ))} </ul> </div> ); } export default App;
Key Differences from Fabric 1.4
- Gateway API: Fabric 2.x uses the simplified
Gatewayclass instead of the oldClientandChannelsetup - Wallet System: Identities are managed via
Wallets(file system, in-memory, etc.) instead of direct keystore paths - Chaincode Lifecycle: If you used the 2.x lifecycle (install -> approve -> commit), this setup works seamlessly—no need to adjust for deprecated
instantiatecalls
Run Everything
- Start your Hyperledger Fabric 2.2 network
- Run the Node.js backend:
node server.js - Run the React frontend:
npm start
内容的提问来源于stack exchange,提问作者Shahzaib Ayyub

