Reputation: 1
Question: How can I sync client-side data (localStorage) with server-side logic using Next.js API routes and WebSocket?
I have a Next.js TypeScript application and I want to use built-in API routes as triggers to interact with my application. Most of my application logic needs to be executed on the client-side because of dependencies on localStorage for state persistence.
I’ve attempted to use WebSocket with EventEmitter, but ran into issues because the WebSocket connection must be initiated at server startup, before the POST request is received. This prevents the shared EventEmitter from updating as expected. Below is the client-side and server-side code I’ve tried:
Logs:
Listener registered for requestId: id3
Event names after registration: ["id1", "id2", "id3"]
Event names before emit: []
Error: Timeout while waiting for response to requestId: id3
Server-side (/api/socket.ts)
if (!res.socket.server.io) {
const io = new Server(res.socket.server);
res.socket.server.io = io;
io.on("connection", (socket) => {
console.log("New client connected");
socket.on(
"process-message",
({ requestId, message }: { requestId: string; message: string }) => {
console.log("Event names before emit:", responseEvents.eventNames());
responseEvents.emit(requestId, message);
}
);
socket.on("disconnect", () => {
console.log("Client disconnected");
});
});
}
if (req.method === "POST") {
const { message } = req.body;
if (!message) {
return res.status(400).json({ error: "Message is required" });
}
const io = res.socket.server.io!;
const requestId = generateSessionId();
console.log(`Request received. Generating requestId: ${requestId}`);
try {
const result = await new Promise<string>((resolve, reject) => {
const listener = (response: string) => {
console.log(`Response received for requestId ${requestId}: ${response}`);
resolve(response);
};
// Register the listener
responseEvents.on(requestId, listener);
console.log("Listener registered for requestId:", requestId);
console.log("Event names after registration:", responseEvents.eventNames());
// Emit the event to WebSocket clients
io.emit("new-message", { requestId, message });
setTimeout(() => {
reject(new Error(`Timeout while waiting for response to requestId: ${requestId}`));
}, 10000);
});
// Send the result as the POST response
res.status(200).json({ success: true, data: result });
} catch (error) {
console.error("Error waiting for client response:", error);
res.status(500).json({ success: false, error: error.message });
}
} else {
res.setHeader("Allow", ["POST"]);
res.status(405).json({ error: `Method ${req.method} Not Allowed` });
}
Client-side (/client.ts)
io = await initializedSocket();
io.on("new-message", ({ requestId, message }: { requestId: string; message: string }) => {
let config = localStorage.getItem("some-key");
let result = await clientSideOperation(config);
io.emit("process-message", { requestId, message });
});
Issue: The WebSocket connection has to be initialized when the server starts, but the POST request comes later. As a result, the shared EventEmitter doesn't update as expected.
Question: Are there any proper techniques or tools I can use to make this WebSocket and EventEmitter implementation work successfully? Specifically, I want to sync the client-side localStorage data with the server-side logic, without sending it as part of the API request.
Upvotes: 0
Views: 30