How to properly setup socket io in MERN app? - sockets

I am new to socket.io. I have basic understanding of how it works, but I am struggling to find proper setup for it within MERN app. If there is any article, or guidance that you can give me, I would appretiate it. I am building social network app, and I need to have live notifications and messages. So I am not sure how to setup socket.io client in react. Should I instanciate it in helper file, like mongoose in express, or is there any other way? Thanks

Install socket.io for server app
Install socket.io - client for client app
import socket.io in server page
const express = require('express');
const http = require('http');
const socketio = require('socket.io');
const cors = require('cors');
const app = express();
const server = http.createServer(app)
const io = socketio(server, { cors: { origin: '*' } }) //for omit cors error
const PORT = 2900;
app.use(express.json());
app.use(cors());
io.on('connect', (socket) => {
console.log("user connected")
socket.on('valor', ({ id, name, }, callback) => {
console.log('data::', id, name)
socket.emit('receiveGreet', { data: 'This message from server' }, (error) => {
console.log('error::', error)
})
callback()
})
socket.on('disconnect', () => {
console.log('user disconnected')
})
})
app.get('/', (req, res) => {
res.json('api running')
})
server.listen(PORT, console.log(`server running in node on port ${PORT}`));
Client side Code May look like this
import io from 'socket.io-client';
let socket: any;
const serverUrl = 'http://localhost:2900';
const MyComponent = () => {
useEffect(() => {
socket = io(serverUrl);
socket.on('receiveGreet', (data) => {
console.log('data::', data);
});
}, []);
return () => {
socket.disconnect();
socket.off();
};
};

Related

Error: connect ECONNREFUSED 127.0.0.1:8000 in Express Js ( When try to Test with Postman )

I am really new to the industry and have this error when trying to check the database connection via API reuests with postman..... Please help me to settle this issue...
I just want to check the mongodb database by sendng API requests. Still I cannot identify the error and I am following a set of tutorials and occure this issue... Anyone can help me to identify the mistake it's highly appreciated....
{ this is dummy text to avoid please add more details...
Here is my code...
const app = express();
const { MongoClient } = require('mongodb');
const PORT = process.env.PORT || 8000;
// Initialize middleware
// we used to install body parser but now it's a built in middleware
// Function of express. It parses incoming JSONpayload
// app.use(express.json({extended:false}));
app.use(express.json({ extended: false }));
// Test Routs
// app.get("/", (req,res)=>res.send("Hello Aruna !!!"));
// app.post("/", (req,res)=>res.send(`Hello ${req.body.name} `));
// app.get("/hello/:name", (req.res)=>res.send(`Hello ${req.params.name}`))
app.get('/api/articles/:name', async (req, res) => {
try {
const articleName = req.params.name;
const client = await MongoClient.connect('mongodb://localhost:27017');
const db = client.db('mernblog');
const articlesinfo = db
.collection('articles')
.findOne({ name: articleName });
res.status(200).jason(articlesinfo);
client.close();
} catch (error) {
res.status(500).jason({ message: 'Error connecting to database', error });
}
});
app.post('/api/articles/:name/add-comments', (req, res) => {
const { username, text } = req.body;
const articleName = req.params.name;
articlesinfo[articleName].comments.push({ username, text });
res.status(200).send(articlesinfo[articleName]);
});
app.post('/', (req, res) => res.send(`Hello ${req.body.name}`));
app.get('/hello/:name', (req, res) => res.send(`Hello ${req.params.name}`));
app.listen(PORT, () => console.log(`Server is running at port ${PORT}`));
Server.js
Terminal
Error and API request in Postman
You have a typo in your code: jason should be json.
Other tips, you should handle your DB connection in a separate method and change your post request since articlesinfo is not a global variable:
const app = express();
const { MongoClient } = require('mongodb');
const PORT = process.env.PORT || 8000;
const client = new MongoClient('mongodb://localhost:27017');
const connectDB = async () => {
try {
await client.connect();
console.log('Successfully connected to DB')
} catch (err) {
await client.close();
console.log('Error connecting to DB');
process.exit(1);
}
}
// Initialize middleware
// we used to install body parser but now it's a built in middleware
// Function of express. It parses incoming JSONpayload
// app.use(express.json({extended:false}));
app.use(express.json({ extended: false }));
// Test Routs
// app.get("/", (req,res)=>res.send("Hello Aruna !!!"));
// app.post("/", (req,res)=>res.send(`Hello ${req.body.name} `));
// app.get("/hello/:name", (req.res)=>res.send(`Hello ${req.params.name}`))
app.get('/api/articles/:name', async (req, res) => {
try {
const articleName = req.params.name;
const db = client.db('mernblog');
const articlesinfo = db
.collection('articles')
.findOne({ name: articleName });
res.status(200).json(articlesinfo);
client.close();
} catch (error) {
res.status(500).json({ message: 'Error connecting to database', error });
}
});
app.post('/api/articles/:name/add-comments', (req, res) => {
const { username, text } = req.body;
const articleName = req.params.name;
const db = client.db('mernblog');
const articlesinfo = db
.collection('articles')
.updateOne({ name: articleName }, { $push: { comments: { username, text } } });
res.status(200).send(articlesinfo);
});
app.post('/', (req, res) => res.send(`Hello ${req.body.name}`));
app.get('/hello/:name', (req, res) => res.send(`Hello ${req.params.name}`));
connectDB();
app.listen(PORT, () => console.log(`Server is running at port ${PORT}`));

Express and MongoDB without Mongoose

This is not so much of a question but more of a consult request. I couldn't find resources to check my method's validity so I would like to hear MongoDB experts' opinion.
I was playing around with MongoDB and came up with this middleware method to pass client to my routes. I have this Express middleware:
const addClientToRequest = async (req, _, next) => {
const client = new MongoClient(uri);
await client.connect();
req.client = client;
next();
};
app.use(addClientToRequest);
After that, I use req.client in my routes to access my database.
app.get("/:id", async (req, res) => {
const client = req.client;
const id = req.params.id;
try {
const data = await client.db("mydb").collection("mycollection").findOne({ id });
if (data) return res.status(200).json(data);
} catch (error) {
return res
.status(500)
.json({ message: "Error fetching requested data", error });
}
return res.status(404).json({ message: "Requested data cannot be found" });
});
What would be a problem in this approach? Is it okay to use MongoDB client like this?
In my experience, we have always defined a separate utility to load a connection pool at the app startup and then reused those connections.
In the above approach, you seem to be creating a new connection for every HTTP request that is made and then not terminating (or) closing the connection. This may be expensive for a large app.
db.util.js
const { MongoClient } = require("mongodb");
const uri = `mongodb://${process.env.DB_USER}:${process.env.DB_PASSWORD}#localhost:27017/${process.env.DATABASE}?maxPoolSize=2-&w=majority`;
const client = new MongoClient(uri);
const init = async () => {
try {
await client.connect();
console.log("Connected");
} catch (error) {
console.log(error);
}
};
const getClient = () => {
return client;
};
module.exports.init = init;
module.exports.getClient = getClient;
app.js
//Import modules
require("dotenv").config({ path: __dirname + "/.env" });
const express = require("express");
const dogRoutes = require("./routes/dog.routes");
const db = require("./utils/db.util");
// Define PORT for HTTP Server
const PORT = 9900;
// Initialize Express
const app = express();
app.use(express.json());
app.use(dogRoutes);
(async () => {
await db.init();
app.listen(PORT, (err) => {
console.log(`Server is up at localhost ${PORT}`);
});
})();
I think that what you could do is to put the client outside of the middleware, so you doesn't re define it and re connect to it each time a request is done.
To do so, simply define it and connect before the middleware, and in the middleware, set the client as req.mongoClient or how you want to name it.
const client = new MongoClient(uri);
await client.connect(); // if this is outside of an async function, either use an async function like (async () => {..script..})(), either define a variable isClientReady and set it on true after the promise resolved.
const addClientToRequest = (req, _, next) => {
req.client = client;
next();
};
app.use(addClientToRequest);

Why axios doesn't work with static nodejs server?

I'm having trouble getting my axios .get in production with React.
I created a nodeJS server with express to render my react page each time I want to refresh my page. That work.
But the problem is that it block my axios .get(). So, my page show normaly without the data I normaly get in dev mode.
BackEnd => server.js with sequelize module to manage my database
const express = require('express');
const app = express();
const cors = require('cors');
const path = require('path');
app.use(express.json());
app.use(cors());
const db = require('./models');
app.use(express.static(path.join(__dirname, 'build')));
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
const governmentCreateRouter = require('./routes/GovernmentCreate');
app.use("/governmentCreate", governmentCreateRouter);
db.sequelize.sync().then(() => {
app.listen();
});
BackEnd => GovernmentCreate.js
const express = require('express');
const router = express.Router();
const { GovernmentCreate } = require('../models');
router.get("/", async (req, res) => {
const listOfGovernments = await GovernmentCreate.findAll({
include: {all: true}
});
res.json(listOfGovernments);
});
FrontEnd => Part of code inside my GouvernmentWall.js that is called with url https://www.mon-gouvernement.fr/gouvernement-galerie
const [governmentList, setGovernmentList] = useState([]);
axios.get(`https://www.mon-gouvernement.fr/GovernmentCreate`)
.then((res) => {
console.log(res.data);
const resData = res.data;
setGovernmentList(sortResData);
}).catch((err) => {
console.log(err);
});
After multi-searching I'm thinking that the problem come from these lines in my server.js :
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
But if I remove it, when I try to refresh my page I'm getting a white page diplay Cannot GET /gouvernement-galerie.
So I'm stocking with this issu. I need your help to move forward.
Problem solve !
As #jonrsharpe mentioned, I have to switch my .get('/*' to the bottom of my routes. My server.js look like :
const express = require('express');
const app = express();
const cors = require('cors');
const path = require('path');
app.use(express.json());
app.use(cors());
const db = require('./models');
app.use(express.static(path.join(__dirname, 'build')));
//************************************************
// ROUTES
//************************************************
const governmentCreateRouter = require('./routes/GovernmentCreate');
app.use("/governmentCreate", governmentCreateRouter);
app.get('/*', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
db.sequelize.sync().then(() => {
app.listen();
});

Socket Not Listening with React Native and Express

BELOW MOBILE SIDE CODE CAN WORK ON IOS AND ANDROID HOWEVER GET WARNING 'Setting a time for long period of time, ie multiple minutes, is a performance and correctness issue on Android as it keeps the timer module awake, and timers can only be called......'.
By passing in the connectionConfig, i am able to get sockets working on both my android and ios simulator. However, i get a warning instead. Even though i commented out reconnection, reconnectionDelay and reconnectionAttempts, i still get those warnings. Anyone with experience on this and can share?
import React from "react";
import { Text, View, Button } from "react-native";
import SocketIO from "socket.io-client";
const connectionConfig = {
// jsonp: false,
// reconnection: true,
// reconnectionDelay: 100,
// reconnectionAttempts: 100000,
transports: ["websocket"] // you need to explicitly tell it to use websockets
};
export default class HomeScreen extends React.Component {
constructor() {
super();
this.socket = SocketIO("http://192.168.0.182:3000", connectionConfig);
// this.socket = SocketIOClient("http://localhost:3000");
this.socket.on("connect", () => {
console.log("connected to server");
});
this.socket.emit("channel1", "Channel1 emitting");
}
btnPress = () => {
console.log("Pressed");
this.socket.emit("channel1", "Emitting when pressing button");
};
render() {
return (
<View>
<Text>Hey</Text>
<Button title="Emit" onPress={this.btnPress} />
</View>
);
}
}
BELOW MOBILE SIDE CODE CANNOT WORK FOR ME
Im trying to implement a basic socket with react-native on frontend and express on backend.
My codes below doesn't seem to work. I emitted some communication from react-native but the server side doesn't seem to be listening as there is no console log.
Anyone can point out my error? Thanks
React-Native
import React from "react";
import { View, Text } from "react-native";
import SocketIOClient from "socket.io-client";
class HomeScreen extends React.Component {
constructor(props) {
super(props);
this.socket = SocketIOClient("http://localhost:3000");
this.socket.emit("channel1", "Hi Server");
}
render() {
return (
<View>
<Text>Home</Text>
</View>
);
}
}
export default HomeScreen;
Express and Node
const express = require("express");
const path = require("path");
const http = require("http");
const socketIO = require("socket.io");
const publicPath = path.join(__dirname, "../public");
const port = process.env.PORT || 3000;
const app = express();
const server = http.createServer(app);
const io = socketIO(server);
const { generateMessage } = require("./utils/message");
app.use(express.static(publicPath));
// Server listens to Client Connected
io.on("connection", socket => {
console.log("New User Connected");
//! Server emits to Client
// socket.emit("newMessage", generateMessage('Admin', 'Welcome!!'));
//! Server emits to Client
socket.emit("welcome", generateMessage("Admin", "Welcome to the Chat App!!"));
//! Server broadcast to Other Clients
socket.broadcast.emit(
"welcome",
generateMessage("Admin", "New User Joined!!")
);
//! Server Listing to Client Event
socket.on("createMessage", message => {
console.log("createMessage", message);
//! Emit to All Clients upon Server Listening
io.emit("newMessage", generateMessage(message.from, message.text));
//! Broadcast to All Clients (except emitter) upon Server Listening
socket.broadcast.emit(
"newMessage",
generateMessage(message.from, message.text)
);
});
socket.on("channel1", message => {
console.log("channel1", message);
});
//! Listening to Client then Server Acknowledging
socket.on("createMessageWithAcknowledgement", (message, callback) => {
console.log("createMessageWithAcknowledgement", message);
//! Emit to All Clients upon Server Listening
io.emit("newMessage", generateMessage(message.from, message.text));
callback({ admin: "Jack", role: "admin" });
});
//! Server listens to Client Disconnect
socket.on("disconnect", () => {
console.log("User Disconnected");
});
});
server.listen(port, () => {
console.log(`Server is up on port ${port}!`);
});
WORKING SOLUTION
REACT NATIVE
import React from "react";
import { Text, View, Button } from "react-native";
import SocketIO from "socket.io-client";
const connectionConfig = {
// jsonp: false,
// reconnection: true,
// reconnectionDelay: 100,
// reconnectionAttempts: 100000,
transports: ["websocket"] // you need to explicitly tell it to use websockets
};
export default class HomeScreen extends React.Component {
constructor() {
super();
this.socket = SocketIO("http://192.168.0.182:3000", connectionConfig);
// this.socket = SocketIOClient("http://localhost:3000");
this.socket.on("connect", () => {
console.log("connected to server");
});
this.socket.emit("channel1", "Channel1 emitting");
}
btnPress = () => {
console.log("Pressed");
this.socket.emit("channel1", "Emitting when pressing button");
};
render() {
return (
<View>
<Text>Hey</Text>
<Button title="Emit" onPress={this.btnPress} />
</View>
);
}
}
NODE & EXPRESS SERVER
const express = require("express");
const path = require("path");
const http = require("http");
const socketIO = require("socket.io");
const publicPath = path.join(__dirname, "../public");
const port = process.env.PORT || 3000;
const app = express();
const server = http.createServer(app);
const io = socketIO(server, {
pingTimeout: 30000,
pingInterval: 30000
});
const { generateMessage } = require("./utils/message");
app.use(express.static(publicPath));
// Server listens to Client Connected
io.on("connection", socket => {
console.log("New User Connected");
//! Server emits to Client
// socket.emit("newMessage", generateMessage('Admin', 'Welcome!!'));
//! Server emits to Client
socket.emit("welcome", generateMessage("Admin", "Welcome to the Chat App!!"));
//! Server broadcast to Other Clients
socket.broadcast.emit(
"welcome",
generateMessage("Admin", "New User Joined!!")
);
//! Server Listing to Client Event
socket.on("createMessage", message => {
console.log("createMessage", message);
//! Emit to All Clients upon Server Listening
io.emit("newMessage", generateMessage(message.from, message.text));
//! Broadcast to All Clients (except emitter) upon Server Listening
socket.broadcast.emit(
"newMessage",
generateMessage(message.from, message.text)
);
});
socket.on("channel1", message => {
console.log("channel1", message);
});
//! Listening to Client then Server Acknowledging
socket.on("createMessageWithAcknowledgement", (message, callback) => {
console.log("createMessageWithAcknowledgement", message);
//! Emit to All Clients upon Server Listening
io.emit("newMessage", generateMessage(message.from, message.text));
callback({ admin: "Jack", role: "admin" });
});
//! Server listens to Client Disconnect
socket.on("disconnect", () => {
console.log("User Disconnected");
});
});
server.listen(port, () => {
console.log(`Server is up on port ${port}!`);
});
On a sidenote, adding below to your server side code will fix the timer warning. (I have already included below code amendment to my code solution above)
const io = socketIO(server, {
pingTimeout: 30000,
pingInterval: 30000
});

How to use mockgoose (or any other db mocking) in express app integration test

Using mockgoose in a simple unit test is quite straight-forward. However I'm a bit fuzzy as to how one would go about using mockgoose or other mocking solutions in an acceptance or integration test.
Given a simple express/MongoDB app like the following:
/*app.js*/
const express = require('express')
const app = express()
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var greetingSchema = mongoose.Schema({
greeting: String
});
var Greeting = mongoose.model('Greeting', greetingSchema);
app.get('/', function (req, res) {
Greeting.find({greeting: 'Hello World!'}, function (err, greeting){
res.send(greeting);
});
});
app.listen(3000, function () {
console.log('Example app listening on port 3000!')
})
and a simple integration test like this:
/*test.js*/
const app = require('app.js');
const request = require('supertest');
it('sends "Hello World!" on the response body', (done) => {
request(app)
.get('/')
.expect(200, 'Hello World!', done);
});
});
By using the actual app in the request, we are connecting to the app's database ('mongodb://localhost/test'). How then can one use mockgoose, or any other solution, to mock the MongoDB database and still run an integration test like the one shown above?
I had the same problem as you. In my case, I solved using chai + chai-http and breaking the db connection and app in different files:
db.js:
const mongoose = require('mongoose');
const config = require('../../config');
mongoose.Promise = global.Promise;
mongoose.set('debug', process.env.DEBUG != undefined);
function open(){
return new Promise((resolve, reject) => {
if(process.env.DEBUG != undefined) {
let Mockgoose = require('mockgoose').Mockgoose;
let mockgoose = new Mockgoose(mongoose);
mockgoose.helper.setDbVersion("** your mongodb version **");
mockgoose.prepareStorage().then(function() {
mongoose.connect(config.db_test, (err, res) => {
if (err) return reject(err);
resolve();
});
}).catch(reject);
}else{
mongoose.connect(config.db, (err, res) => {
if (err) return reject(err);
resolve();
});
}
});
}
function close(){
return mongoose.disconnect();
}
module.exports = { close, open };
app.js:
const express = require('express');
const bodyParser = require('body-parser');
const api = require('./routes');
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use('/api', api);
module.exports = app;
test.js (for test):
const chai = require('chai');
const chaiHttp = require('chai-http');
const expect = chai.expect;
const conn = require('./../utils/db'); // <-- db.js
const app = require('../../app'); // <-- app.js
chai.use(chaiHttp);
describe('# Test', function(){
before(function(done) {
conn.open().then(() => done()).catch(done);
});
after(function(done){
conn.close().then(() => done()).catch(done);
});
it(`test something`, function(done){
chai.request(app) // <-- pass the app here
.get('/path/to/test')
.then((res) => {
// expects
done();
})
.catch((err) => {
done(err);
});
});
});
index.js (for development or production):
const conn = require('./utils/db'); // <-- db.js
const app = require('./app'); // <-- app.js
const config = require('./config');
conn.open().then(() => {
app.listen(config.port, () => {
// OK!
});
});
I hope it works for you or anyone.