Full-Stack Web Application using React, Node Js, Web sockets. My project is based on ReactJs with server on Express. When trying to connect to socket.io from chrome I receive "WebSocket is closed before the connection is established" message.
"editorpage.js"
useEffect(() => {
const init = async () => {
socketRef.current = await initSocket();
socketRef.current.on('connect_error', (err) => handleErrors(err));
socketRef.current.on('connect_failed', (err) => handleErrors(err));
function handleErrors(e) {
console.log('Socket Error', e);
toast.error('Socket Connection Failed, Try Again Later.');
reactNavigator('/');
}
socketRef.current.emit(ACTIONS.JOIN, {
roomId,
username: location.state?.username,
});
// Listening for joined event
socketRef.current.on(
ACTIONS.JOINED,
({ clients, username, socketId }) => {
if (username !== location.state?.username) {
toast.success(`${username} joined the room.`);
console.log(`${username} joined`);
}
setClients(clients);
socketRef.current.emit(ACTIONS.SYNC_CODE, {
code: codeRef.current,
socketId,
});
}
);
// Listening for disconnected
socketRef.current.on(
ACTIONS.DISCONNECTED,
({ socketId, username }) => {
toast.success(`${username} left the room.`);
setClients((prev) => {
return prev.filter(
(client) => client.socketId !== socketId
);
});
}
);
};
init();
return () => {
socketRef.current?.disconnect();
socketRef.current?.off(ACTIONS.JOINED);
socketRef.current?.off(ACTIONS.DISCONNECTED);
};
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
This error when running on google chrome
"socket.js"
import { io } from 'socket.io-client';
export const initSocket = async () => {
const options = {
'force new connection': true,
reconnectionAttempt: 'Infinity',
timeout: 10000,
transports: ['websocket'],
};
return io(process.env.REACT_APP_BACKEND_URL, options);
};
"server.js"
const express = require('express');
const app = express();
const http = require('http');
const {
Server
} = require('socket.io');
const ACTIONS = require('./src/Actions');
const server = http.createServer(app);
const io = new Server(server);
const userSocketMap = {};
function getAllConnectedClients(roomId) {
// Map
return Array.from(io.sockets.adapter.rooms.get(roomId) || []).map(
(socketId) => {
return {
socketId,
username: userSocketMap[socketId],
};
}
);
}
io.on('connection', (socket) => {
console.log('socket connected', socket.id);
socket.on(ACTIONS.JOIN, ({
roomId,
username
}) => {
userSocketMap[socket.id] = username;
socket.join(roomId);
const clients = getAllConnectedClients(roomId);
clients.forEach(({
socketId
}) => {
io.to(socketId).emit(ACTIONS.JOINED, {
clients,
username,
socketId: socket.id,
});
});
});
socket.on(ACTIONS.CODE_CHANGE, ({
roomId,
code
}) => {
socket.in(roomId).emit(ACTIONS.CODE_CHANGE, {
code
});
});
socket.on(ACTIONS.SYNC_CODE, ({
socketId,
code
}) => {
io.to(socketId).emit(ACTIONS.CODE_CHANGE, {
code
});
});
socket.on('disconnecting', () => {
const rooms = [...socket.rooms];
rooms.forEach((roomId) => {
socket.in(roomId).emit(ACTIONS.DISCONNECTED, {
socketId: socket.id,
username: userSocketMap[socket.id],
});
});
delete userSocketMap[socket.id];
socket.leave();
});
});
const PORT = process.env.PORT || 5001;
server.listen(PORT, () => console.log(`Listening on port ${PORT}`));
Related
I'm trying to call Redis from a Twilio Function (serverless) and I don't see incoming connections in my Redis log.
Is this setup viable?
Sample code follows:
const Redis = require('ioredis');
const fs = require('fs');
exports.handler = function (context, event, callback) {
const config = Runtime.getAssets()['config.json'].open();
let redisClientConfig = JSON.parse(config).redisConfig;
let contactCacheTime = JSON.parse(config).contactCacheTime;
if (!redisClientConfig) {
throw new Error('Redis config not set.');
}
const redisClient = new Redis(redisClientConfig);
redisClient.on('error', (err) => {
console.error(`Cannot connect to redis, reason: ${(err.message || err)}`);
});
redisClient.getex('mhn-twilio-bot-contact:'.concat(event.contactKey), 'EX', contactCacheTime)
.then((res) => {
if (!res) {
redisClient.setex('mhn-twilio-bot-contact:'.concat(event.contactKey), contactCacheTime, '<CACHED-VALUE>');
}
callback(null, { cached: res ? true : false });
})
.catch((err) => {
callback(null, { cached: false });
});
};
While using websocket, message that client gets is blob. Whats the best way to access its content? I have tried using FileReader but then it looked like {"user":"whatever","msg":"whatever"} and I would like to get it as {user:"whatever",msg:"whatever"}. Is it even possible?
const submitMessage = (usr, msg) => {
const message = { user: loggedInUser.username, message: msg };
ws.send(JSON.stringify(message));
setMessages([message, ...messages]);
}
useEffect(() => {
ws.onopen = () => {
console.log('WebSocket Connected');
}
ws.onmessage = (e) => {
const message = JSON.parse(e.data);
setMessages([message, ...messages]);
}
return () => {
ws.onclose = () => {
console.log('WebSocket Disconnected');
setWs(new WebSocket(URL));
}
}
}, [ws.onmessage, ws.onopen, ws.onclose, messages]);
**server**
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', function connection(ws) {
ws.on('message', function incoming(data) {
wss.clients.forEach(function each(client) {
if (client !== ws && client.readyState === WebSocket.OPEN) {
client.send(data);
}
});
});
});
What could be the reason that change event is getting called so many times while all I am doing is basic crud on the document ? If change in document then I am refreshing my table by calling serverSideListProject API. and Also, I noticed that connection is getting disconnect frequently, is there any configurations we can make to stop it from disconnecting ?
"socket.io": "^2.2.0" for server-side,"ngx-socket-io": "^3.4.0" for client.
app.module.js:
import { SocketIoModule, SocketIoConfig } from 'ngx-socket-io';
const config: SocketIoConfig = { url: 'http://localhost:6001', options: {} };
DBHandler code:
exports.monitorChanges = function () {
return new Promise((resolve, reject) => {
return getConnection().then((db) => {
if (db == null) {
console.log("db in find() is undefined");
reject();
} else {
const changeStream = db.db(config.mongodb.dbname).collection("project").watch(
[
{ $match: { "operationType": { $in: ["insert", "update", "replace"] } } },
{ $project: { "_id": 1, "fullDocument": 1, "ns": 1, "documentKey": 1 } }
],
{ fullDocument: "updateLookup" }
);
resolve(changeStream)
}
socket connection:
route.js
var express = require('express');
var app = express();
const io = require('socket.io')();
io.on('connection', socket => {
console.log('connected', socket.id)
socket.on('projects', (data) => projectHandler.serverSideListProject(socket, data));
socket.on('addProject', (data) => projectHandler.addProject(socket, data));
socket.on('updateProject', (data) => projectHandler.updateProject(socket, data));
socket.on('deleteProject', (data) => projectHandler.deleteProject(socket, data));
socket.on('disconnect', () => console.log('A user disconnected'));
});
io.on("connect_error", (err) => { console.log(`connect_error due to ${err.message}`) });
I'm using nuxtjs/axios and Mongoose to write to MongoDB. The POST always works but it takes a few seconds for the insert to get into MongoDB. Problem is that I'm trying to call a GET immediately after a new POST so i can get all the latest records. That doesn't always happen because it takes a few seconds for the data to get into the DB. Here's my index.js file for the server:
if (process.env.NODE_ENV !== 'production') {
require('dotenv').config();
}
const Post = require('./models/post');
const express = require('express');
const { Nuxt, Builder } = require('nuxt');
const app = express();
const mongoose = require('mongoose');
const xss = require('xss-clean');
app.use(
express.urlencoded({
extended: true
})
)
app.use(express.json())
app.use(xss());
const config = require('../nuxt.config.js');
config.dev = process.env.NODE_ENV !== 'production';
const nuxt = new Nuxt(config);
const { host, port } = nuxt.options.server;
const username = process.env.username;
const pwd = process.env.pwd;
const server = process.env.server;
const db = process.env.db;
const dbURI = `mongodb+srv://${username}:${pwd}#${server}/${db}?
retryWrites=true&w=majority`;
async function start() {
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
} else {
await nuxt.ready();
}
app.use(nuxt.render);
}
start();
mongoose
.connect(dbURI, {useNewUrlParser: true, useUnifiedTopology: true})
.then((result) => {
app.listen(port, host); // listen
}
)
.catch(err => console.log(err));
app.get('/posts', (req, res) => {
Post
.find()
.sort({createdAt: -1})
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
})
app.post(
'/posts',
(req, res) => {
const post = new Post({
body: req.body.post.trim()
});
post
.save()
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
}
);
I feel like in app.post the .save() isn't waiting for the insert to complete. Is my implementation wrong? Here's my Store:
export const actions = {
async getPosts() {
let res = await this.$axios.get(`/posts`);
return res;
}
}
export const mutations = {
async savePost(state, data) {
let res = await this.$axios.post('/posts', {post: data});
return res;
}
}
And here's my index.vue file:
export default {
components: {},
data: () => ({
posts:[],
confession: ""
}),
mounted(){
this.getPosts();
},
methods: {
async getPosts() {
let res = await this.$store.dispatch('getPosts');
this.posts = res;
},
async savePost(payload) {
let res = await this.$store.commit('savePost', payload);
return res;
},
clear(){
this.confession = "";
},
focusInput() {
this.$refs.confession.focus();
},
onSubmit() {
this
.savePost(this.confession.trim())
.then((result) => {
this.playSound();
this.getPosts();
this.clear();
this.focusInput();
});
},
playSound: function(){
// sound code
}
}
}
}
Maybe you can try to add w: "majority" option in save method.
Mongoose Documentation of save options
MongoDB Documentation for further explanation of 'writeConcern'
app.post(
'/posts',
(req, res) => {
const post = new Post({
body: req.body.post.trim()
});
post
.save({w: "majority"})
.then((result) => {
res.send(result);
})
.catch((err) => console.log(err));
}
);
I am trying to make a real-time shopping list app with react, express and socket.io.
I set socket.io up and everything, but when I open two browser windows and I add an item in one, the other does not updates its shopping list items. Although the console.log("made socket connection") is working.
What did I do wrong?
index.js
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const helmet = require("helmet");
const socket = require("socket.io");
const routes = require("./routes/api");
const app = express();
const port = process.env.port || 9000;
mongoose.connect("mongodb://localhost/shoppinglist", {
useNewUrlParser: true,
useUnifiedTopology: true,
useCreateIndex: true,
useFindAndModify: false,
});
app.use(cors());
app.use(helmet());
app.use(express.json());
app.use("/", routes);
app.use((err, req, res, next) => {
res.status(422).send({
error: err.message,
});
});
const server = app.listen(port, () => {
console.log(`now listening for requests on port ${port}`);
});
const io = socket(server);
io.on("connection", (socket) => {
console.log("made socket connection");
socket.on("item_added", (item) => {
io.sockets.emit("item_added", item);
});
});
ShoppingList.js React component
import React, { Component } from "react";
import AddItem from "./AddItem";
import socketIOClient from "socket.io-client";
class ShoppingList extends Component {
state = {
id: null,
products: [],
socket: socketIOClient(
"http://localhost:9000/" + this.props.match.params.id
),
};
componentDidMount() {
this.setState({
id: this.props.match.params.id,
});
this.socketListen();
this.getItems();
}
socketListen = () => {
this.state.socket.on("item_added", (data) => {
let products = [...this.state.products, data];
this.setState({
products: products,
});
});
};
getItems = () => {
fetch("http://localhost:9000/" + this.props.match.params.id)
.then((data) => {
return data.json();
})
.then((json) => {
this.setState({
products: json,
});
});
};
addItem = (item) => {
fetch("http://localhost:9000/" + this.props.match.params.id, {
headers: {
Accept: "application/json",
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify({
product: item.product,
quantity: item.quantity,
quantityType: item.quantityType,
}),
})
.then((response) => response.json())
.then(() => {
this.getItems();
this.state.socket.emit("item_added", {
product: item.product,
quantity: item.quantity,
quantityType: item.quantityType,
});
});
};
deleteShoppingList = () => {
fetch("http://localhost:9000/" + this.props.match.params.id, {
method: "DELETE",
})
.then((response) => response.json())
.then(() => {});
};
render() {
const itemList = this.state.products.map((product) => {
return (
<div key={product._id}>
<h2>Product: {product.product}</h2>
<h2>Quantity: {product.quantity}</h2>
<h2>Quantity type: {product.quantityType}</h2>
<button onClick={() => this.deleteItem(product)}>Delete</button>
</div>
);
});
return (
<div>
<h1 className="text-4xl font-bold text-white mb-10">Shopping list</h1>
{itemList}
<AddItem addItem={this.addItem} />
<button onClick={this.deleteShoppingList}>Delete Shopping List</button>
</div>
);
}
}
export default ShoppingList;