400 error socket.io when trying to connect from client side - node.js

I am setting up a little chat feature with socket.io
for some reason when I try to connect from client I am getting this error
I am initiating my connection in a chatProvider component in a react app.
Here is the client side connection function
import React, { useState, useEffect} from 'react';
import io from "socket.io-client";
const ChatProvider = ({ children }) => {
useEffect(() => {
const socket = io('http://localhost:5000');
socket.on('messageFromServer', (dataFromServer) => {
console.log(dataFromServer);
socket.emit('messageToServer', {data: "this is from client"})
})
}, [])
return <ChatContext.Provider value={value}>{children}</ChatContext.Provider>;
};
ChatProvider.propTypes = { children: PropTypes.node.isRequired };
export default ChatProvider;
Here is my backend
const express = require('express');
const mongoose = require('mongoose');
const cookieSession = require('cookie-session');
const passport = require('passport');
const bodyParser = require('body-parser') // when a post request comes in to express, it doesnt automatically parse, you need to call body-parser
const keys = require('./config/keys');
const cors = require('cors');
const { setIO } = require('./helpers/socket-setup');
require("dotenv").config();
require('./models/GoogleUserModel'); // the user model must be placed before this services passport// this must be ran after requiring model bcuz this needs the model. ORDER
require('./models/UserModel');
require('./services/passport');
const app = express()
const server = require('http').createServer(app)
const corsOptions = {
origin:"http://localhost:3000",
credentials: true, //access-control-allow-credentials:true
optionSuccessStatus:200
}
app.use(cors(corsOptions))
mongoose.Promise = global.Promise;
mongoose.connect(keys.mongoURI, {
useNewUrlParser: true,
useCreateIndex: true,
useUnifiedTopology: true
})
mongoose.connection.on('error', () => {
throw new Error (`unable to connect to database: ${keys.mongoURI}`)
});
app.use(bodyParser.json({limit: '10mb'}))
app.use(express.urlencoded( { extended: true }))
app.use(
cookieSession({
maxAge: 30 * 24 * 60 * 60 * 1000,
keys: [keys.cookieKey]
})
)
app.use(passport.initialize());
app.use(passport.session());
require('./routes/webhookRoutes')(app)
require('./routes/userRoutes')(app);
let io = setIO(server)
io.on("connection", socket => {
socket.emit('messageFromServer', { data: 'this is from server' });
socket.on('messageToServer', ( dataFromClient )=> {
console.log(dataFromClient);
})
})
const PORT = process.env.PORT || 5000;
server.listen(PORT);
Here is a helper file socket-setup.js I made so I can use socket.io functions within other files, could this be the issue?
const socket = require("socket.io")
let _io;
const setIO = (server) => {
_io = socket(server, {
cors : {
origin:"http://localhost:3000",
credentials: true, //access-control-allow-credentials:true
optionSuccessStatus:200
}
})
return _io
}
const getIO = () => {
return _io
}
module.exports = {
getIO,
setIO
}

Related

Server not functioning in mern heroku deployment

I have a semi successful deployment to heroku but the calls to the server are saying CONNECTION REFUSED and I can't figure it out.
I can hit the route and it returns correctly in postman but not in production
Below is the services file (the ones with /api in front are the ones being called)enter image description here
`import http from "../utils/http-common";
class CountriesService {
getAll() {
return http.get("/api/country");
}
getAllCountries() {
return http.get("/country/getAll");
}
getScroll(skip) {
return http.get(`/country?skip=${skip}`);
}
get(id) {
return http.get(`/country/${id}`);
}
create(countryForm) {
return http.post("/country/new", countryForm);
}
edit(id, values) {
return http.put(`/country/${id}`, values);
}
delete(id) {
return http.delete(`/country/${id}`);
}
}
export const getPostsPage = async (pageParam = 1, options = {}) => {
const response = await http.get(`/api/country?_page=${pageParam}`, options)
return response.data
}
export default new CountriesService();
`
This is the http-common
import axios from "axios";
var url;
process.env.REACT_APP_NODE_ENV === "production"
? (url = "")
: (url = "http://localhost:5000/");
export default axios.create({
baseURL: `${url}`,
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
});
this is the server file on the backend with a proxy from the frontend to localhost 5000
const path = require('path');
require("dotenv").config();
const express = require("express");
const app = express();
const mongoose = require("mongoose");
const cors = require("cors");
var morgan = require("morgan");
const MongoDBStore = require('connect-mongo');
const mongoSanitize = require('express-mongo-sanitize');
const helmet = require('helmet');
// Models
const User = require("./models/user.js");
// Passport Config
const passport = require('passport');
const LocalStrategy = require('passport-local').Strategy;
const flash = require("connect-flash");
const session = require("express-session");
const cookieParser = require("cookie-parser");
// const { MongoClient } = require("mongodb");
const BodyParser = require("body-parser");
const { storage } = require("./imageupload/cloudinary");
const userAuthRoute = require("./routes/user.js");
const reviewRoute = require("./routes/review.js");
const countryRoute = require("./routes/country.js");
const cityRoute = require("./routes/city.js");
const activityRoute = require("./routes/activity.js");
const restaurantRoute = require("./routes/restaurant.js");
const dishesRoute = require('./routes/dishes.js');
const outdoorsRoute = require('./routes/outdoors.js');
const apiRoutes = require("./routes/api.js");
const cityReviewRoute = require("./routes/cityReviews.js");
const foodRoute = require('./routes/food.js');
const landmarkRoute = require('./routes/landmark.js');
const searchRoute = require('./routes/search.js');
const contactRoute = require('./routes/contact.js');
const db_url = process.env.DB_URL;
const PORT = process.env.PORT || 5000;
const secret = process.env.SESSION_SECRET;
// const client = new MongoClient(process.env.DB_URL);
const corsOptions = {
origin: [
"http://localhost:3000",
"http://localhost:5000",
"https://geopilot.herokuapp.com",],
credentials: true,
optionSuccessStatus:200,
};
app.use(cors(corsOptions));
app.use(express.json());
app.use(mongoSanitize({ replaceWith: "_" }));
app.use(BodyParser.json());
app.use(express.urlencoded({ extended: true }));
app.use(morgan("tiny"));
// app.use(cookieParser());
mongoose
.connect(db_url)
.then(() => {
console.log("database connection established successfully");
})
.catch((error) => console.log("this is the error", error));
const store = MongoDBStore.create({
mongoUrl: process.env.DB_URL,
ttl: 24 * 60 * 60 * 365, // 1 year
autoRemove: 'native',
crypto: {
secret,
},
});
store.on('error', function(error) {
console.log('SESSION STORE ERROR:', error);
});
// Session Settings
const sessionOptions = {
name: "geopilot_session",
secret: secret,
store: store,
resave: false,
saveUninitialized: false,
cookie: {
samesite: false,
// httpOnly: true,
// secure: true,
expires: Date.now() + 1000 * 60 * 60 * 24 * 365,
maxAge: 1000 * 60 * 60 * 24 * 365,
},
};
// app.set('trust proxy', 1) // trust first proxy
// Session Setup
app.use(session(sessionOptions));
// Helmet Setup
// app.use(helmet())
// Passport Middleware
app.use(passport.initialize());
app.use(passport.session());
passport.use(new LocalStrategy(User.authenticate()));
passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());
// Routes
app.use("/api/country/:countryId/reviews", reviewRoute);
app.use("/api/country", countryRoute);
app.use('/api/city', cityRoute);
app.use('/api/city/:cityId/reviews', cityReviewRoute);
app.use('/api/activity', activityRoute);
app.use('/api/restaurant', restaurantRoute);
app.use('/api/restaurant/:restaurantId/reviews', reviewRoute);
app.use('/api/landmark', landmarkRoute);
app.use('/api/landmark/:landmarkId/reviews', reviewRoute);
app.use('/api/food', foodRoute);
app.use('/api/dishes', dishesRoute);
app.use('/api/outdoor', outdoorsRoute);
app.use('/api/search', searchRoute);
app.use('/api/user', userAuthRoute);
app.use('/api/contact', contactRoute);
// ----------- Deployment -----------
__dirname = path.resolve();
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "../client/build")));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "../client", "build", "index.html"));
});
}
// ----------- Deployment -----------
app.get("*", () => {
res.send("PAGE NOT FOUND");
});
I tried requesting in postman which works.
I tried changing things in the package.json to help proxy or run server.
I tried to switch endpoints and change CORS policy but it won't work.
For anyone seeing this and is stuck, I changed http-common baseUrl to match my website url (not localhost) and then had to do "npm run build" to make it actually implement the changes.

can't get express session data from socket io connection

Hello i'm having a problem to access express session data like I did on http connection.
Even when I copy the documentation edited by socket io on this link
https://socket.io/how-to/use-with-express-session#1st-use-case-socketio-only-retrieves-the-session-context i still can't access express session data.
there is my server
const express = require('express');
const app = express();
const cors = require('cors');
const helmet = require('helmet');
const sessionStrategy = require('#middlewares/session_strategy/sessionStrategy');
const httpServer = require("http").Server(app);
const Server = require("socket.io").Server;
app.use(express.urlencoded({
parameterLimit: 20,
extended: true,
limit: '10mb'
}));
app.use(express.json({limit: '100mb'}));
// CORS
app.use(cors({
origin: true,
credentials: true
}));
app.use(sessionStrategy);
if (process.env.NODE_ENV === 'production') {
app.set('trust proxy', 1);
}
const corsConfig = {
origin: true,
credentials: true,
methods: ["GET", "POST"]
};
const io = new Server(httpServer, {cors: corsConfig});
const wrap = middleware => (socket, next) => middleware(socket.request, {}, next);
io.use(wrap(sessionStrategy));
io.on("connection", (socket) => {
// socket.request.session give undefined value
console.log("Connexion", socket.request.session);
socket.on("event_test", (data) => {
console.log(data);
console.log(socket);
});
socket.on("connect_error", (err) => {
console.log(`connect_error due to ${err.message}`);
});
});
app.all('*', require("#middlewares/catchAllRoute"));
httpServer.listen(process.env.NODE_API_PORT, function () {
console.log('App listening on port ' + process.env.NODE_API_PORT + ' ...');
});
and there is my client
import { io } from "socket.io-client";
const socket = io("http://localhost:9000", {
withCredentials: true
});
export default socket;
import socket_connection from "#/sockets_connections";
// the command is launched after a click
socket_connection.emit("event_test", {"test":"test"});

socket.io problems. Not able to connect. front end says connection: false and backend doesn't log anything

i am new to socket.io and i can't get it to connect to react app. here is my app.js in node
const express = require('express');
const port = process.env.PORT || 4000;
const router = require('./routes/routes');
const cors = require('cors');
const app = express();
const bodyParser = require('body-parser');
const db = require('./db/db');
const server = require('http').createServer(app);
const io = require('socket.io')(server);
io.on('connection', () => {
console.log('connected');
});
app.use('*', cors());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
(router);
app.listen(port, () => {
console.log('listening on port ' + port);
db.sync({
// force: true,
logging: false,
});
});
and my front end code.
import React, { useState, useEffect, useRef } from 'react';
import { io } from 'socket.io-client';
import classes from './Chatroom.module.css';
const Chatroom = ({ user, getAllMessages, setGetAllMessages }) => {
const ENDPOINT = 'http://localhost:4000/getallmessages';
var socket = io(ENDPOINT);
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
socket.on('connect', () => {
socket.send('hello');
console.log('connected.');
});
console.log(socket);
}, []);
Whenever i look in the console on it shows connected: false and nothing is logging on the backend.
In order to fix the issue i had to add options to my io declaration as follows.
const server = require('http').createServer(app);
const options = {
cors: true,
origins: ['http://127.0.0.1:3000'],
};
const io = require('socket.io')(server, options);
127.0.0.1 being home and on client side my server is on 3000 so that's where that comes from. and on the client side you were right i had to remove "getallmessages" route so now it is as follows.
onst ENDPOINT = 'http://localhost:4000/';
var socket = io(ENDPOINT);
const messagesEndRef = useRef(null);
const scrollToBottom = () => {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
};
useEffect(() => {
socket.on('connect', () => {
socket.send('hello');
console.log('connected.');
});
console.log(socket);
}, []);
socket.io is bound to the server object so you should listen to the server instead of the app.
Change app.listen to server.listen
Change endpoint by removing getallmessages if you are not using namespaces

my function does not execute by the router

I have a router and it should execute my function when the user wants to access /matches URL, but it doesn't
this is my controller that my function is wroten in there
const matchModel = require(`./../models/matchModel`);
//This function will get matches
exports.getMatches = async (req, res) => {
const matches = await matchModel.find();
res.status(200).json({
status: "success",
results: matches.length,
data: {
matches,
},
});
};
this is my route handler that should execute the function in the controller
const express = require("express");
const matchController = require(`./../controllers/matchController`);
const router = express.Router();
router.route("/matches").get(matchController.getMatches);
module.exports = router;
and this is my app file that adds URLs to middleware
const express = require("express");
const matchRouter = require(`./routes/matchRoutes`);
const app = express();
app.use(express.json());
//importing routers
app.use("/matches", matchRouter);
module.exports = app;
and after all, I run this server.js file to run my app
const mongoose = require("mongoose");
const dotenv = require("dotenv");
//setting up dotenv setting and
dotenv.config({ path: "./config.env" });
const conString = process.env.DATABASE_CON_STRING;
const port = process.env.PORT;
const app = require(`./app`);
mongoose
.connect(conString, {
useNewUrlParser: true,
useCreateIndex: true,
useFindAndModify: false,
useUnifiedTopology: true,
})
.then(() => {
console.log("connection to DB has been established successfully!");
});
app.listen(port, () => {
console.log("server is running...");
});
You are loading the router under /matches
app.use("/matches", matchRouter);
Then you are loading the /matches route under that router
router.route("/matches").get(matchController.getMatches);
That is going to make the route /matches/matches.
You probably want it to be router.route(''). This will be the index route under the /matches router.

NodeJS/Express Server on Heroku, NextJS Client on Vercel, SocketIO Problem

I’ve got a problem with my NextJS + NodeJS + SocketIO setup and i wrap my head around it since days.
In development mode on my mac machine everything is fine but in production there is the problem.
The NodeJS server is hosted on Heroku and the NextJS client is hosted on Vercel.
Server:
require('dotenv').config()
// Packages
const express = require('express')
const mongoose = require('mongoose')
const passport = require('passport')
const cookie = require('cookie')
const jwtDecode = require('jwt-decode')
const cors = require('cors')
// App
const app = express()
// Models
const User = require('./models/User')
// App Settings
app.use(cors())
app.use(express.urlencoded({ limit: '10mb', extended: true }))
app.use(express.json({ limit: '10mb', extended: true }))
app.use(passport.initialize())
// App Routes
app.use('/_admin', require('./routes/_admin'))
app.use('/auth', require('./routes/auth'))
app.use('/profile', require('./routes/profile'))
app.use('/posts', require('./routes/posts'))
app.use('/comments', require('./routes/comments'))
app.use('/search', require('./routes/search'))
app.use('/users', require('./routes/users'))
require('./utils/passport')(passport)
const db = process.env.MONGO_URI
const port = process.env.PORT || 5000
mongoose
.connect(db, {
useNewUrlParser: true,
useFindAndModify: false,
useCreateIndex: true,
useUnifiedTopology: true
})
.then(() => {
console.log('MongoDB Connected') // eslint-disable-line no-console
const server = app.listen(port, () => console.log(`Server running on port ${port}`)) // eslint-disable-line no-console
const io = require('socket.io')(server)
io.on('connection', async socket => {
const decodedUser =
socket.handshake.headers.cookie && cookie.parse(socket.handshake.headers.cookie).jwtToken
? jwtDecode(cookie.parse(socket.handshake.headers.cookie).jwtToken)
: null
if (decodedUser) {
console.log(`${socket.id} -> ${decodedUser.username} -> connected`) // eslint-disable-line no-console
const user = await User.findById(decodedUser.id)
if (!user.sockets.includes(socket.id)) {
user.sockets.push(socket.id)
user.dateOnline = Date.now()
user.isOnline = true
user.save()
}
socket.on('disconnect', async () => {
console.log(`${socket.id} -> ${decodedUser.username} -> disconnected`) // eslint-disable-line no-console
const user = await User.findById(decodedUser.id)
const index = user.sockets.indexOf(socket.id)
user.sockets.splice(index, 1)
if (user.sockets.length < 1) {
user.isOnline = false
user.dateOffline = Date.now()
}
user.save()
})
} else {
console.log(`${socket.id} -> GUEST -> connected`) // eslint-disable-line no-console
socket.on('disconnect', async () => {
console.log(`${socket.id} -> GUEST -> disconnected`) // eslint-disable-line no-console
})
}
})
})
.catch(err => console.log(err)) // eslint-disable-line no-console
Client React Context:
import React, { createContext, useContext, useEffect, useState } from 'react'
import PropTypes from 'prop-types'
import io from 'socket.io-client'
const SocketContext = createContext()
export function useSocket() {
return useContext(SocketContext)
}
export function SocketContextProvider({ children }) {
const [socket, setSocket] = useState(null)
useEffect(() => {
setSocket(io(process.env.NOIZE_APP_SERVER_URL))
}, [])
const defaultContext = { socket }
return <SocketContext.Provider value={defaultContext}>{children}</SocketContext.Provider>
}
SocketContextProvider.propTypes = {
children: PropTypes.node
}
The hole React app is wrapped is this context-provider and as i said, on my localhost everything works fine.
The problem on the Heroku server is, that it is not receiving the cookie with my bearer jwt token from client in the SocketIO handshake. I’m lost right now and hope for help/hints/and so on…
Thank you very much =)
I solved the problem!
cookies.set('jwtToken', jwtToken, {
path: '/',
domain: process.env.NODE_ENV === 'development' ? 'localhost' : 'example.com'
})
The cookie needs the domain attribute in this case because the server runs on api.example.com and the client on www.example.com.

Resources