I am implementing logic that receives real-time stock prices from an external websocket (KIS WebSocket) and sends this information to the front end.
The structure is: React (frontend) - Nest.js (backend) - KIS (external WebSocket)
However, the connection to KIS Websocket works well, and the real-time price of the item to be viewed is communicated well, but only pingpong data is received.
like this: terminal image
I am curious why the real-time prices of subscribed items are not received from KIS WebSocket. When I tested it with KIS WebSocket using the WebSocket test tool, the real-time price was received well, so it seems to be an internal problem in the server I implemented.
thank you in advance.
React Code :
import React, { useEffect, useState } from "react";
import io from "socket.io-client";
interface ChartRealTimePriceProps {
stockCode: string;
}
const ChartRealTimePrice: React.FC<ChartRealTimePriceProps> = ({ stockCode }) => {
const [price, setPrice] = useState<string>("");
useEffect(() => {
const fetchCurrentPrice = async () => {
try {
const response = await fetch(`${process.env.REACT_APP_API_SERVER_URI}/company/current-price/${stockCode}`);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
setPrice(data.result);
} catch (error) {
console.error("Error fetching data:", error);
}
};
fetchCurrentPrice();
}, [stockCode]);
useEffect(() => {
const webSocket = io(`${process.env.REACT_APP_WEBSOCKET_SERVER_WS_URI}`, {
withCredentials: true,
transports: ["websocket"],
});
webSocket.on("connect", () => {
console.log("Connected to WebSocket server");
webSocket.emit("messageToServer", { stockCode });
});
webSocket.on("disconnect", () => {
console.log("Disconnected from WebSocket server");
});
webSocket.on("connect_error", (error) => {
console.error("WebSocket connect error:", error);
});
webSocket.on("error", (error) => {
console.error("WebSocket error:", error);
});
webSocket.on("message", (message) => {
console.log("WebSocket message:", message);
if (message.event === "messageFromKSI" && message.data.stockCode === stockCode) {
setPrice(message.data.price);
}
});
return () => {
if (webSocket.connected) {
webSocket.close();
}
};
}, [stockCode]);
if (price === "") {
return <div>Loading...</div>;
}
return <div>{price}</div>;
};
export default ChartRealTimePrice;
Nest Code : ws.gateway.ts :
import {
WebSocketGateway,
WebSocketServer,
SubscribeMessage,
OnGatewayConnection,
OnGatewayDisconnect,
OnGatewayInit,
} from "@nestjs/websockets";
import { Server, Socket } from "socket.io";
import { ExternalWsService } from "./external-ws.service";
import { Injectable, Logger } from "@nestjs/common";
@Injectable()
@WebSocketGateway({
cors: {
origin: "ws://localhost:3000",
methods: "GET,POST",
credentials: true,
},
})
export class WsGateway implements OnGatewayInit, OnGatewayConnection, OnGatewayDisconnect {
@WebSocketServer() server: Server;
private clients: Map<string, Socket> = new Map();
private readonly logger = new Logger(WsGateway.name);
afterInit() {
this.logger.log("WebSocket Server Initialized ✅");
}
constructor(private externalWsService: ExternalWsService) {
// KSI WebSocket Message to Clients
this.externalWsService.onMessage((data) => {
try {
const parsedMessage = JSON.parse(data.toString());
this.logger.log(` message from KSI WebSocket: ${JSON.stringify(parsedMessage)}`);
} catch (error) {
this.logger.error(`Error parsing KSI message: ${error}`);
}
this.broadcastToClients("messageFromKSI", data);
});
}
@SubscribeMessage("messageToServer")
handleMessage(client: Socket, payload: any): void {
this.logger.log(`Received message from ${client.id}: ${payload}`);
// Message to KSI Websocket Server
this.externalWsService.sendMessage(payload.stockCode);
}
broadcastToClients(event: string, message: any) {
this.server.emit(event, message);
}
handleConnection(client: Socket) {
this.logger.log(`Client connected: ${client.id}`);
this.clients.set(client.id, client);
}
handleDisconnect(client: Socket) {
this.logger.log(`Client disconnected: ${client.id}`);
this.clients.delete(client.id);
}
}
external-ws.service.ts :
import { Injectable, Logger } from "@nestjs/common";
import WebSocket from "ws";
import * as dotenv from "dotenv";
dotenv.config({ path: `.env.${process.env.NODE_ENV}` });
@Injectable()
export class ExternalWsService {
private externalWebSocket: WebSocket;
private connectedKSIWebSocket = false;
private readonly logger = new Logger(ExternalWsService.name);
constructor() {
this.connectKSIWebSocket();
}
private connectKSIWebSocket() {
if (this.connectedKSIWebSocket) {
this.logger.log("Already connected to KSI WebSocket server");
return;
}
try {
this.externalWebSocket = new WebSocket(`${process.env.KIS_WEBSOCKET_SERVER_URL}`);
this.externalWebSocket.on("open", () => {
this.logger.log("Connected to KSI WebSocket server");
this.connectedKSIWebSocket = true;
});
this.externalWebSocket.on("close", () => {
this.logger.log("Disconnected from KSI WebSocket server");
this.connectedKSIWebSocket = false;
setTimeout(() => this.connectKSIWebSocket(), 5000);
});
this.externalWebSocket.on("error", (error) => {
this.logger.error("Error with KSI WebSocket connection:", error);
});
} catch (error) {
this.logger.error(`Failed to connect to KSI WebSocket server: ${error.message}`);
}
}
sendMessage(message: any) {
if (this.externalWebSocket && this.externalWebSocket.readyState === WebSocket.OPEN) {
const header = {
approval_key: "fb73e061-2c57-48e7-96b5-80f65b7dd5a1",
custtype: "P",
tr_type: "1",
"content-type": "utf-8",
};
const body = {
input: {
tr_id: "H0IFCNT0",
tr_key: message,
},
};
this.externalWebSocket.send(JSON.stringify({ header, body }));
} else {
this.logger.warn("Cannot send message, WebSocket is not open");
}
}
onMessage(callback: (data: any) => void) {
if (this.externalWebSocket) {
this.externalWebSocket.on("message", callback);
} else {
this.logger.error("WebSocket is not initialized");
}
}
}