The Crypto Shop Backend uses Socket.io to provide real-time notifications when blockchain transactions are confirmed. This guide covers client-side setup and connection management.
Server Configuration
The Socket.io server is initialized when the backend starts:
~/workspace/source/src/config/socket.js:5-12
export const initializeSocket = ( server ) => {
io = new Server ( server , {
cors: {
origin: [ 'http://localhost:3000' , 'http://localhost:5173' , process . env . FRONTEND_URL ],
credentials: true ,
methods: [ 'GET' , 'POST' ]
}
});
// ...
};
CORS Configuration
The server accepts connections from:
http://localhost:3000 (default React dev server)
http://localhost:5173 (Vite dev server)
process.env.FRONTEND_URL (production frontend URL)
Set FRONTEND_URL in your .env file to allow your production frontend to connect: FRONTEURL = https://your-frontend-domain.com
Client Installation
Install the Socket.io client in your frontend application:
npm install socket.io-client
Basic Client Setup
React Example
Create a Socket.io service for your React application:
import { io } from 'socket.io-client' ;
const SOCKET_URL = process . env . REACT_APP_API_URL || 'http://localhost:3000' ;
class SocketService {
constructor () {
this . socket = null ;
}
connect () {
this . socket = io ( SOCKET_URL , {
withCredentials: true ,
transports: [ 'websocket' , 'polling' ]
});
this . socket . on ( 'connect' , () => {
console . log ( 'Connected to Socket.io server:' , this . socket . id );
});
this . socket . on ( 'disconnect' , ( reason ) => {
console . log ( 'Disconnected from Socket.io server:' , reason );
});
this . socket . on ( 'connect_error' , ( error ) => {
console . error ( 'Connection error:' , error . message );
});
return this . socket ;
}
disconnect () {
if ( this . socket ) {
this . socket . disconnect ();
this . socket = null ;
}
}
joinUserRoom ( userId ) {
if ( this . socket ) {
this . socket . emit ( 'join-user' , userId );
}
}
onTransactionConfirmed ( callback ) {
if ( this . socket ) {
this . socket . on ( 'transaction:confirmed' , callback );
}
}
offTransactionConfirmed ( callback ) {
if ( this . socket ) {
this . socket . off ( 'transaction:confirmed' , callback );
}
}
}
export default new SocketService () ;
Vue.js Example
import { io } from 'socket.io-client' ;
const socket = io ( import . meta . env . VITE_API_URL || 'http://localhost:3000' , {
withCredentials: true ,
transports: [ 'websocket' , 'polling' ]
});
socket . on ( 'connect' , () => {
console . log ( 'Socket connected:' , socket . id );
});
export default {
install ( app ) {
app . config . globalProperties . $socket = socket ;
}
} ;
Vanilla JavaScript
import { io } from 'socket.io-client' ;
const socket = io ( 'http://localhost:3000' , {
withCredentials: true ,
transports: [ 'websocket' , 'polling' ]
});
socket . on ( 'connect' , () => {
console . log ( 'Connected:' , socket . id );
});
socket . on ( 'disconnect' , () => {
console . log ( 'Disconnected' );
});
Joining User Rooms
After authentication, join the user-specific room to receive personalized notifications:
~/workspace/source/src/config/socket.js:17-20
socket . on ( 'join-user' , ( userId ) => {
socket . join ( `user: ${ userId } ` );
console . log ( `[Socket] User ${ userId } joined room user: ${ userId } ` );
});
Client Implementation
// After user logs in
const userId = '507f1f77bcf86cd799439011' ; // From authentication
socket . emit ( 'join-user' , userId );
Users must join their room to receive transaction:confirmed events. The room format is user:{userId}.
Connection Lifecycle
React Hook Example
Create a custom hook for Socket.io integration:
import { useEffect , useRef } from 'react' ;
import socketService from '../services/socket' ;
import { useAuth } from './useAuth' ; // Your auth hook
export const useSocket = () => {
const { user } = useAuth ();
const socketRef = useRef ( null );
useEffect (() => {
// Connect when component mounts
socketRef . current = socketService . connect ();
// Join user room if authenticated
if ( user ?. id ) {
socketService . joinUserRoom ( user . id );
}
// Cleanup on unmount
return () => {
socketService . disconnect ();
};
}, [ user ?. id ]);
return socketRef . current ;
};
Usage in components:
src/components/OrderPage.jsx
import { useEffect } from 'react' ;
import { useSocket } from '../hooks/useSocket' ;
import socketService from '../services/socket' ;
function OrderPage () {
const socket = useSocket ();
useEffect (() => {
const handleTransactionConfirmed = ( data ) => {
console . log ( 'Transaction confirmed!' , data );
// Update UI, show notification, etc.
};
socketService . onTransactionConfirmed ( handleTransactionConfirmed );
return () => {
socketService . offTransactionConfirmed ( handleTransactionConfirmed );
};
}, []);
return (
< div >
{ /* Your order page UI */ }
</ div >
);
}
Connection Options
Recommended Options
const socket = io ( SOCKET_URL , {
// Enable credentials for CORS
withCredentials: true ,
// Transport methods (try WebSocket first, fallback to polling)
transports: [ 'websocket' , 'polling' ],
// Auto-reconnect settings
reconnection: true ,
reconnectionDelay: 1000 ,
reconnectionDelayMax: 5000 ,
reconnectionAttempts: 5 ,
// Timeout settings
timeout: 20000 ,
// Path (if your server uses a custom path)
path: '/socket.io/'
});
Development vs Production
const socket = io ( 'http://localhost:3000' , {
withCredentials: true ,
transports: [ 'websocket' , 'polling' ],
reconnection: true
});
Error Handling
Handle connection errors gracefully:
socket . on ( 'connect_error' , ( error ) => {
console . error ( 'Connection failed:' , error . message );
if ( error . message === 'xhr poll error' ) {
// Polling transport failed
console . log ( 'Retrying with WebSocket only...' );
socket . io . opts . transports = [ 'websocket' ];
}
});
socket . on ( 'connect_timeout' , () => {
console . error ( 'Connection timeout' );
});
socket . on ( 'error' , ( error ) => {
console . error ( 'Socket error:' , error );
});
Testing Connection
Verify your Socket.io setup:
// Test connection
socket . on ( 'connect' , () => {
console . log ( '✓ Connected to server' );
console . log ( 'Socket ID:' , socket . id );
console . log ( 'Transport:' , socket . io . engine . transport . name );
});
// Test user room join
socket . emit ( 'join-user' , 'test-user-id' );
console . log ( '✓ Joined user room' );
// Test event listener
socket . on ( 'transaction:confirmed' , ( data ) => {
console . log ( '✓ Received transaction:confirmed event' );
console . log ( 'Data:' , data );
});
Server-Side Connection Handling
The server handles connections as follows:
~/workspace/source/src/config/socket.js:14-25
io . on ( 'connection' , ( socket ) => {
console . log ( `[Socket] User connected: ${ socket . id } ` );
socket . on ( 'join-user' , ( userId ) => {
socket . join ( `user: ${ userId } ` );
console . log ( `[Socket] User ${ userId } joined room user: ${ userId } ` );
});
socket . on ( 'disconnect' , () => {
console . log ( `[Socket] User disconnected: ${ socket . id } ` );
});
});
Best Practices
Connect Once Per Application
Create a singleton Socket.io instance and reuse it across your app: // ✓ Good: Single instance
const socket = io ( SOCKET_URL );
export default socket ;
// ✗ Bad: Multiple instances
function Component () {
const socket = io ( SOCKET_URL ); // Creates new connection each render!
}
Always remove event listeners when components unmount: useEffect (() => {
const handler = ( data ) => console . log ( data );
socket . on ( 'transaction:confirmed' , handler );
return () => {
socket . off ( 'transaction:confirmed' , handler );
};
}, []);
Re-join user rooms after reconnection: socket . on ( 'connect' , () => {
if ( userId ) {
socket . emit ( 'join-user' , userId );
}
});
Use Environment Variables
Configure Socket.io URL via environment variables: REACT_APP_API_URL = http://localhost:3000
Next Steps
Socket.io Events Learn about available events and payloads
Transaction Listener Understand how transactions are monitored