Socket.IO
/
Real-Time Events
/
Auction System
Baraka Socket Server
Complete integration guide for the real-time auction socket server. Covers all client-emitted events, server-emitted events, payload shapes, database models, and connection flow.
⚡ Socket.IO
● NestJS WebSocketGateway
🔒 JWT Auth on Connection
🔔 Room-Based Broadcasting
How the socket layer fits into the Baraka system
The Baraka socket server powers all real-time features in the live auction flow. It is built with NestJS + Socket.IO and uses MySQL via Prisma ORM for persistence. Every auction runs inside a dedicated Socket.IO room (auction_{'{'}auctionId{'}'}), so events are scoped to participants.
Transport
Socket.IO (WebSocket + HTTP long-polling)
CORS Policy
All origins allowed (*)
Room Naming
auction_{'{'}auctionId{'}'}
Authentication on Socket
JWT access token (verified on connect)
Database ORM
Prisma (MySQL)
🔒
Authentication: Every connection is validated in handleConnection. The server verifies the JWT access token and stores userId on client.data. Event handlers read client.data.userId directly — do not pass userId in event payloads. Connections with a missing or invalid token are immediately disconnected.
How to establish a Socket.IO connection from the frontend
Base URL (development)
ws://localhost:3000
Namespace
/ (default, no custom namespace)
JavaScript / TypeScript — Connect with JWT
import { io } from 'socket.io-client';
const accessToken = '<YOUR_ACCESS_TOKEN>';
const socket = io('http://<SERVER_URL>', {
auth: { token: accessToken },
transports: ['websocket'],
reconnection: true,
reconnectionAttempts: 5,
});
socket.on('connect', () => {
console.log('Connected:', socket.id);
});
socket.on('disconnect', () => {
console.log('Disconnected');
});
socket.on('error', ({ message }) => {
console.error('Auth error:', message);
});
ℹ
The server verifies the token in handleConnection. On success, userId is stored server-side on client.data and used for all subsequent events automatically. On disconnect, the client is removed from all auction rooms and viewer counts are updated.
⚠
If no token is provided, or if the token is invalid or expired, the server emits error with the message "No token provided" or "Invalid or expired token" and immediately disconnects the client. Obtain a fresh token via POST /auth/refresh and reconnect.
Step-by-step sequence for a live auction session
1
Auction owner emits startLiveAuction → server sets isLive = true, broadcasts auctionStarted to all
2
Owner emits changeCuurentProduct to set the first product, price, and minimum bid → room receives auction_change_product
3
Viewers emit joinAuction → each client joins room auction_{'{'}auctionId{'}'}, room receives userCountUpdate
4
Participants emit placeBid → all room members receive newBid with the updated bids list
5
Participants emit comment → all room members receive newComment with full comments list
6
Owner emits awardingAuction with winning product & weight → room receives auctionEnded with winner details, bids & comments are cleared
7
Owner repeats step 2–6 for next products until done, then emits endAuction → room receives auctionFinished
!
At any point owner can emit cancelAuction → room receives auctionCanceled
Events your frontend emits to the socket server
Sets the auction's isLive flag to true in the database. Broadcasts auctionStarted to all connected clients (not just room members). Must be called before anyone can join.
Payload
| Field | Type | Required | Description |
| auctionId | number | yes | ID of the auction to start |
Example
socket.emit('startLiveAuction', {
auctionId: 42,
});
ℹ
On success, all clients (globally) receive auctionStarted with the updated Auction object. On failure, only the emitting client receives error.
Checks that the auction is live, adds the client to the Socket.IO room auction_{'{'}auctionId{'}'}, and increments the user count. If the auction is not live yet, an error is sent back.
Payload
| Field | Type | Required | Description |
| auctionId | number | yes | Auction to join |
Example
socket.emit('joinAuction', {
auctionId: 42,
});
⚠
If isLive === false, the server emits an error back: "Auction has not started yet."
Removes the client from the room and decrements the viewer count. Also triggers automatically on socket disconnect.
Payload
| Field | Type | Required | Description |
| auctionId | number | yes | Auction to leave |
Example
socket.emit('leaveAuction', {
auctionId: 42,
});
Notifies the room that the broadcaster (auction owner) has left the live video stream. The server validates ownership before broadcasting.
Payload
| Field | Type | Required | Description |
| auctionId | number | yes | Auction ID |
Example
socket.emit('broadCasterLeave', {
auctionId: 42,
});
Validates the bid amount against the last bid, persists it to Auction_bids, and broadcasts the updated bid list to the room.
Payload
| Field | Type | Required | Description |
| auctionId | number | yes | Target auction |
| amount | number | yes | Bid amount. Must be strictly greater than the last bid. |
Example
socket.emit('placeBid', {
auctionId: 42,
amount: 1500,
});
⚠
If amount <= lastBid, the server returns: "Bid must be higher than the current bid (X)"
Sets isCanceled = true and isLive = false. Notifies all room members.
Payload
| Field | Type | Required | Description |
| auctionId | number | yes | Auction to cancel |
Example
socket.emit('cancelAuction', {
auctionId: 42,
});
Sets isEnded = true and isLive = false. Used to close the auction session without awarding. For awarding a winner use awardingAuction.
Payload
| Field | Type | Required | Description |
| auctionId | number | yes | Auction to end |
Example
socket.emit('endAuction', {
auctionId: 42,
});
Finds the highest bid for the current product, creates a winnings record, clears all bids & comments for the auction, then resets the current product fields back to empty. Broadcasts the result to the room.
Payload
| Field | Type | Required | Description |
| auctionId | number | yes | Auction ID |
| product | string | yes | Name/description of the product being awarded |
| weight | string | yes | Weight of the product (stored in winnings) |
Example
socket.emit('awardingAuction', {
auctionId: 42,
product: 'Gold Ring – 18K',
weight: '5.2g',
});
ℹ
After awarding, bids and comments are deleted from the database to prepare for the next product round. The auction itself stays live — use changeCuurentProduct to move to the next product.
Updates the auction's current_product, bidPrice, minBidPrice, and actualPrice in the database, then broadcasts the new state to all room members.
⚠
Note the typo in the event name: changeCuurentProduct (double u). Match this exactly.
Payload
| Field | Type | Required | Description |
| auctionId | number | yes | Auction ID |
| product | string | yes | Product name / description |
| minBidPrice | number | yes | Minimum accepted bid amount |
| bidPrice | number | yes | Starting/current bid price |
| actualPrice | number | yes | Actual market price of the product |
Example
socket.emit('changeCuurentProduct', {
auctionId: 42,
product: 'Silver Necklace – 925',
minBidPrice: 800,
bidPrice: 1000,
actualPrice: 1400,
});
Events the server emits that your frontend must listen for
Emitted globally (not room-scoped) when an auction goes live. Use this to update the auction list UI for all users.
Response Payload — Auction object
| Field | Type | Description |
| id | number | Auction primary key |
| title | string | Auction title |
| isLive | boolean | Will be true |
| … | | All other Auction model fields (see Database Models section) |
Listen Example
socket.on('auctionStarted', (auction) => {
console.log('Auction is now live!', auction);
});
Triggered whenever a user joins, leaves, or disconnects from an auction room. Show live viewer count.
Response Payload
| Field | Type | Description |
| auctionId | number | Which auction this count belongs to |
| userCount | number | Current number of connected viewers |
Listen Example
socket.on('userCountUpdate', ({ auctionId, userCount }) => {
console.log(`Viewers in auction ${auctionId}: ${userCount}`);
});
Fired after a bid is validated and persisted. Returns the new bid and the full bids list sorted newest-first.
Response Payload
| Field | Type | Description |
| newBid | Auction_bids & { user } | The newly placed bid with user relation |
| auctionBids | (Auction_bids & { user })[] | All bids for this auction ordered createdAt DESC (newest first) |
newBid fields
| Field | Type | Description |
| id | number | Bid ID |
| auction_id | number | Parent auction |
| user_id | number | Bidder |
| bid | number | Bid amount |
| createdAt | DateTime | When bid was placed |
| user | users | Full user object |
Listen Example
socket.on('newBid', ({ newBid, auctionBids }) => {
console.log(`New bid: ${newBid.bid} by ${newBid.user.name}`);
setBids(auctionBids);
});
The auction was canceled by the owner. Redirect or dismiss the live screen.
Response Payload
| Field | Type | Description |
| auctionId | number | The canceled auction |
| message | string | "The auction has been canceled." |
Listen Example
socket.on('auctionCanceled', ({ auctionId, message }) => {
showAlert(message);
navigate('/auctions');
});
The auction was closed normally via endAuction (without awarding). No winner is declared.
Response Payload
| Field | Type | Description |
| auctionId | number | The finished auction |
| message | string | "The auction has been ended." |
Listen Example
socket.on('auctionFinished', ({ auctionId, message }) => {
showAlert(message);
});
A product round was awarded via awardingAuction. Contains the winning bid, winning user, and the reset auction state. Bids & comments are already cleared at this point.
Response Payload
| Field | Type | Description |
| auction | Auction & { Auction_bids, Auction_comments } | Updated auction object with empty bids and comments arrays |
| winningBid | Auction_bids & { user } | null | The highest bid record. null if no bids were placed. |
| winningUser | users | null | Full user record of the winner. null if no bids. |
Listen Example
socket.on('auctionEnded', ({ auction, winningBid, winningUser }) => {
if (winningUser) {
console.log(`Winner: ${winningUser.name} with bid ${winningBid.bid}`);
} else {
console.log('No bids placed.');
}
});
Notifies room members that the active product has changed. Update the product display and reset bid input accordingly.
Response Payload
| Field | Type | Description |
| product | string | New current_product value |
| bidPrice | number | Starting bid price |
| minBidPrice | number | Minimum acceptable bid |
| actualPrice | number | Actual market price |
Listen Example
socket.on('auction_change_product', (data) => {
setCurrentProduct(data.product);
setMinBid(data.minBidPrice);
setStartingBid(data.bidPrice);
setActualPrice(data.actualPrice);
});
The auction owner has left the live video stream. Show a "Broadcaster disconnected" message to viewers.
Response Payload
| Field | Type | Description |
| auctionId | number | The affected auction |
Listen Example
socket.on('broadCasterLeft', ({ auctionId }) => {
showBanner('The broadcaster has left. Please wait...');
});
Sent privately to the client that triggered an invalid action. Always listen for this to surface server-side validation errors in your UI.
Response Payload
| Field | Type | Description |
| message | string | Human-readable error reason |
Common Error Messages
| Trigger | Error Message |
| connection | No token provided |
| connection | Invalid or expired token |
| startLiveAuction | Unauthorized or Auction not found |
| joinAuction | Auction has not started yet. |
| placeBid | Bid must be higher than the current bid (X) |
| placeBid | Auction not found! / User not found! |
| cancelAuction | You only cancel live auctions! |
| cancelAuction | Auction is already canceled |
| cancelAuction | You are not allowed to cancel this auction! |
| endAuction | Auction is already Ended |
| awardingAuction | You are not allowed to end the auction |
| awardingAuction / changeCuurentProduct | Auction could not be ended. |
Listen Example
socket.on('error', ({ message }) => {
showToast(message, 'error');
});
Prisma schema definitions for all models involved in socket responses
ℹ
When socket responses include nested objects (e.g. user inside a bid), these are Prisma relation includes — the full model is returned. Models below reflect the exact database schema.
| Field | Type | Notes |
| idPK | Int | Auto-increment |
| title | String | |
| description | String | |
| type | AuctionType | Live | Open |
| current_product | String? | Active product name |
| product_description | String? | |
| actualPrice | Int | Market price |
| minBidPrice | Int | Minimum bid allowed |
| bidPrice | Int | Starting bid price |
| quantity | Int | Default 1 |
| expiryDate | DateTime | |
| startDate | DateTime? | |
| isLive | Boolean | Default false |
| isExpired | Boolean | Default false |
| isCanceled | Boolean | Default false |
| isEnded | Boolean | Default false |
| image_url | String | Cover image |
| user_idFK | Int? | → users.id (creator) |
| winning_user_idFK | Int? | → users.id (winner) |
| category_idFK | Int | → categories.id |
| createdAt | DateTime | Auto now |
| updatedAt | DateTime | Auto update |
| Auction_bids | Auction_bids[] | Relation — included in auctionEnded |
| Auction_comments | Auction_comments[] | Relation — included in auctionEnded |
| Field | Type | Notes |
| idPK | Int | Auto-increment |
| name | String | Display name |
| type | userType | Normal | Expert |
| category_idFK | Int? | → categories.id |
| number | String | Phone number |
| otpCode | String? | Temporary OTP |
| createdAt | DateTime | |
| updatedAt | DateTime | |
| Field | Type | Notes |
| idPK | Int | Auto-increment |
| auction_idFK | Int | → Auction.id (cascade delete) |
| user_idFK | Int | → users.id (cascade delete) |
| bid | Int | Bid amount |
| createdAt | DateTime | |
| updatedAt | DateTime | |
| user | users | Relation — always included in socket response |
⚠ All bids are deleted after awardingAuction
| Field | Type | Notes |
| idPK | Int | Auto-increment |
| auction_idFK | Int | → Auction.id (cascade delete) |
| user_idFK | Int | → users.id (cascade delete) |
| comment | String | Comment text |
| createdAt | DateTime | |
| updatedAt | DateTime | |
| user | users | Relation — always included in socket response |
⚠ All comments are deleted after awardingAuction
| Field | Type | Notes |
| idPK | Int | Auto-increment |
| user_idFK | Int | → users.id (winner) |
| auction_idFK | Int? | → Auction.id |
| product | String | Product name awarded |
| price | Int | Winning bid amount |
| weight | String? | Product weight |
| sold | Boolean | Default false |
| createdAt | DateTime | |
| updatedAt | DateTime | |
✓ Created on every successful awardingAuction call
| Field | Type | Notes |
| idPK | Int | |
| name | String | Category name |
| pic_url | String | Category image |
| createdAt | DateTime | |
| updatedAt | DateTime | |
Baraka Socket Server Documentation ·
Generated from src/modules/socket/socket.gateway.ts & prisma/schema.prisma ·
Keep this file in sync when the gateway changes.