How To Make Socket Connection To Heroku Server In Socket.io? - node.js

I'm made a full stack mern application and hosted the nodejs backend in heroku and the frontend is running on netlify. All .env variables are set up both for heroku and netlify, I set up cors() also. The website has chat functionality so I use socket.io for that which that used to work nice in development.
This is link to my heroku server (backend api) https://ural-shop.herokuapp.com/
In messenger.js file (This is where the socket connections happens)
import React, { useEffect, useState, useRef } from "react";
import io from "socket.io-client";
const socketRef = useRef();
useEffect(() => {
socketRef.current = io.connect("https://ural-shop.herokuapp.com/");
},[]);
index.js file (main file of backend)
const express = require("express");
const app = express();
const http = require("http");
const server = http.createServer(app);
const socket = require("socket.io");
app.use(cors());
const io = socket(server, {
cors: { origin: "https://ural-shop.herokuapp.com/" },
});
server.listen(PORT, () => console.log(`Server on port ${PORT}`));
I only put the neccessary parts of the files.
From google developer console I see this.
Developer Console Output
Error From Network Section Of Developer console

Looking at your screenshot it looks like you've not configured cors correctly on your backend. Either put * as value so requests from anywhere are allowed, or put the URL of your frontend application instead of the URL of your backend API. (https://ural-shop.herokuapp.com/ is what you've configured and it should be the URL of your frontend application)
Let me know if it worked.

I had the same issue with the same tech stack. And this took me 3 days to find out this was all CORS issue. But you need to include your Heroku server in your CORS whitelist.
According to the official docs:
Since Socket.IO v3, you need to explicitly enable Cross-Origin
Resource Sharing (CORS).
client-site
So, everything you have to do is to specify the correct URL according to the environment. If you want to set only with the production URL, it will work both in the local or prod environment.
Mine is dynamic like this:
const IS_PROD = process.env.NODE_ENV === "production";
const URL = IS_PROD ? "yoursite.herokuapp.com" : "http://localhost:5000";
const socket = io(URL);
server-side
...
const io = socket(server, {
cors: {
origin: ["http://localhost:3000", "yoursite.herokuapp.com"],
},
});
Very important: You also have to make sure that you development is in sync with your production side. Otherwise, you will test it and see the same error, but only because you didn't update your production, too. Yep, this kind of silly mistakes can happen...
Hope that helps someone in the same situation. This solved my issue.

Related

Why 'blocked by CORS policy' in React, but not with Node?

Running the exact same script in React is blocked by Cors policy, but not when I run it with node.
Why can it be done with node, but not with React?
How can I still fetch this in a React app?
It's also allowed in this chrome extension, is that because the script is executed visiting youtube so that domain is youtube?
async function isThisAllowed() {
const videoURL = `https://www.youtube.com/watch?v=QnQe0xW_JY4`
const data = await axios.get(videoURL);
console.log(data);
}
CORS is a way for websites (in this case YouTube) to prevent other domains from accessing them. Basically, in this case, scripts running on other domains will not be able to access youtube.com.
CORS is only enforced by browsers, so it makes sense that your Node script would work as expected while your React app wouldn't.
Here's a good explanation of how CORS works.
You could fix this with a simple Node proxy:
import express from "express";
import axios from "axios";
const app = express();
app.get("/proxy", async (req, res) => {
const resp = await axios.get(req.query.url);
res.send(resp.data);
});
app.listen(process.env.PORT || 3000);

net::ERR_SSL_PROTOCOL_ERROR on http POST to express server

I'm trying to post from a react client to an express server on localhost and I get this error in google chrome.
react app: http://localhost:3000
express app: http://localhost:5000
POST https://localhost:5000/upload net::ERR_SSL_PROTOCOL_ERROR
I don't have any SSL https self signed certificate, but do I need to? I have used create-react-app inside a directory within my express application to bootstrap my front-end so I'm unaware if maybe a webpack setting somewhere is trying to handle http as https for this particular operation?
I haven't used any bootstrapping for my express js application and I'll include my server file below for reference.
const express = require ('express')
const bodyParser = require ('body-parser')
const port = 5000
var app = express()
var cors = require("cors");
app.use(cors())
app.use(bodyParser())
app.listen(port, () => console.log(`Example app listening on port ${port}!`))
app.post('/upload', (req,res)=>{
if (req.files === null ){
return res.status(400).json({message: 'no image uploaded please try again'})
}else{
const file = req.files.file
let body = req.body
res.json({body})
}
})
I know there are a fair few questions on this issue but they weren't relevant or didn't provide me any insight. I have tried making sure the ports are allowed through my firewall aswell. I have also cleared my browser cache.
I had the same issue and changed the URL to http://localhost:5000/upload instead of using https://localhost:5000/upload.
This has been discussed in multiple posts
By default you cannot call localhost request on browsers but you can disable the security
Check here : Disable same origin policy in Chrome
I also faced the same issue and I changed the URL to http://localhost:5000/path instead of using https://localhost:5000/path
It works for me.
I had the same issue but it was with WAGMI (react hooks for web3 dev) and instead of using "https://localhost:6969" (this was the RPC url for my local node) I used "http://localhost:6969" which worked!

Express server not redirecting HTTP to HTTPS

I am attempting to setup a server with ExpressJS which uses HTTPS and servers a React app. I want any HTTP requests to be redirected to using HTTPS.
Additional constraint: I am using React router, so the server needs to be able to handle that. e.g. if I request localhost:3000/profile, I want React Router to handle that, I just need Express to server up index.html as I had gone to localhost:3000.
Problem: I think I've been able to setup HTTPS (Chrome complains but I don't mind for now), but I cannot get redirection to work.
For comparison, this is my code for how I setup my HTTP-only server for development (before I ever tried to setup HTTPS):
const express = require('express');
const http = require('http');
const path = require('path');
const app = express();
const DIST_DIR = path.resolve('./dist');
app.use(express.static(DIST_DIR));
app.get('*', (req, res) => {
res.sendFile(path.resolve(DIST_DIR, './index.html'));
});
const devServer = http.createServer(app);
devServer.listen(3000);
Next, I started with this guide. I created a self-signed SSL certificate then set up my application. I then looked at some examples of how to redirect, such as this question.
However, it doesn't seem to be working.
Here is my code at present:
app.use(express.static(DIST_DIR));
app.use((req, res, next) => {
if (req.secure) {
next();
} else {
res.redirect(`https://${req.headers.host}${req.url}`);
}
});
app.get('*', (req, res) => {
res.sendFile(path.resolve(DIST_DIR, './index.html'));
});
const httpServer = http.createServer(app);
httpServer.listen(3080);
const privateKey = // uses FS to get my key
const certificate = // uses FS to get my cert
const credentials = { key: privateKey, cert: certificate };
const httpsServer = https.createServer(credentials, app);
httpsServer.listen(3443);
I can access https://localhost:3443 and navigate the app as expected, and Express properly handles refreshes on pages like /profile. Great. Chrome complains that "CA root certificate is not trusted. Install this cert in the trusted root certification authorities store" but I haven't put in the work to solve that, because in a real production environment I'd be provided the certificate and key from a trusted source.
However, when I go to http://localhost:3080, I just end up at http://localhost:3080. Chrome devtools shows I'm not using HTTPS. Furthermore, I can't go directly to /profile, as Chrome gives me the error "This site can’t provide a secure connection".
I've tried other methods listed in that stackoverflow question I linked, but they either have the same behavior or straight up don't work. I'm a bit out of my element here and I'm trying to learn, but I don't understand why this isn't working. Any help would be appreciated. Thanks.
While you can manage this in your application it is often the convention to have a web server like nginix or apache in front of your application that manages the https redirection. Depending on your setup it is also common to manage your certificates at this front server to simplify certificate management. If you are going to deploy onto aws or another cloud provider I would let their infrastructure handle this for you.

How to make API request from react client to express server running on the Heroku platform

Ive been trying to deploy a Twitch like application using react, redux, node media server and json server module to Heroku. However, I keep running into a issue when trying to connect my react client and express server via a api request, during production.
Im trying to make the actual request through my action creators and by using axios with a base url of http://localhost:4000, however that only works on my local machine.
const response = await streams.get("/streams");
dispatch({ type: FETCH_STREAMS, payload: response.data });
};
You can view my full repo at https://github.com/XorinNebulas/Streamy
You can also view my current deployed version of the site on Heroku at
https://streamy-app.herokuapp.com/
here is my api/server.js file. My express server will be watching on a random port equal to process.env.PORT, so I have no way of knowing how to make a network request via my action creators to that random port during production.
const path = require("path");
const cors = require("cors");
const jsonServer = require("json-server");
const server = jsonServer.create();
const router = jsonServer.router("db.json");
const middlewares = jsonServer.defaults({
static: "../client/build"
});
const PORT = process.env.PORT || 4000;
// Set default middlewares (logger, static, cors and no-cache)
server.use(cors());
server.use(middlewares);
if (process.env.NODE_ENV === "production") {
// Add custom routes before JSON Server router
server.get("*", (req, res) => {
res.sendFile(
path.resolve(__dirname, "../", "client", "build", "index.html")
);
});
}
// Use default router
server.use(router);
server.listen(PORT, () => {
console.log(`JSON Server is listening on port ${PORT}`);
});
I expected the request to go thru and load up some data from api/db.json, with a resquest url of https://streamy-app.herokuapp.com/streams but instead i got a request url of http://localhost:4000/streams, which of course leads to the CORS issue below
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at http://localhost:4000/streams. (Reason: CORS request did not succeed).
I would truly appreciate any suggestions, been working on this for days.
Alright looks like I figured it out. I simply went into streams/client/package.json and added
"proxy":"http://localhost:4000"
I then went into streams\client\src and deleted the api folder which contained my custom axios with a base url. using instead axios out of the box for my action creators
const response = await axios.get("/streams");
dispatch({ type: FETCH_STREAMS, payload: response.data });
};
Now while running locally in development mode, I'm able to make a request to http://localhost:4000/streams, but after deploying my node app to Heroku I successfully make a request over to https://streamy-app.herokuapp.com/streams
hope this helps someone with slimier issues.
First, you should know that Heroku doesn't allow to expose multiple ports, which means you should change the approach of multiple ports to something else (see this answer).
Second, the file client/src/apis/streams.js is hard-coded configured to send requests to http://localhost:4000/ - which is not a good idea.
Whatever approach you choose - even deploying to another host server - you will need to dynamically configure the API endpoint, per environment.
I would also recommend you to:
Change the way you deploy react, as explained here.
After doing the above, consider consolidating your API service with the static server, so that you don't need multiple ports, and then everything becomes easier.

Heroku node + react app: anchor tag auth request unhandled

I'm building a node + express server, with create-react-app to the frontend.
I used passportjs for auth routes handling, and all the stuff totally working on localhost ( backend on port 5000 and frontend on port 3000, with a proxy ).
When I deploy to Heroku, seems like the server can't recognize my auth routes and so heroku serve up static index.html.
If I test my APIs with Postman all seems to work ( I can see the html page for google oauth ), but with an anchor tag in my react app or manually writing the endpoint in the url, I can see only the same page reloading.
My server index.js:
const express = require('express')
const passport = require('passport')
const mongoose = require('mongoose')
const path = require('path')
// KEYS
const keys = require('./config/keys')
// MONGOOSE MODELS
require('./models/User')
mongoose.connect(keys.mongoURI)
// PASSPORT SETUP
require('./config/passport')
// CREATE THE SERVER
const app = express()
// EXTERNAL MIDDLEWARES
require('./middlewares/external')(app)
// ROUTE HANDLERS
require('./routes/authRoutes')(app)
// PRODUCTION SETUP
if (process.env.NODE_ENV === 'production') {
// express serve up production assets ( main.js, main.css )
app.use(express.static('client/build'))
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
})
}
// START THE SERVER
const PORT = process.env.PORT || 5000
app.listen(PORT)
Flow:
LOCALHOST:
react app starts -> I click 'Google Login' -> GET request to "/auth/google" -> google oauth flow -> redirect to "/" and my react app reappears, the user is logged in.
HEROKU:
react app on my-app.herokuapp.com/ -> click on "Google Login" -> the page reloads, nothing happens. the user is not logged in.
Please guys, help me.
Thanks
This is a result of the service worker being installed by default to make your app a Progressive Web App
To determine if this is an issue for you, test your heroku production mode app in incognito mode. The request for /auth/google should now reach the server and behave as it does in development.
Once you determine it is an issue, you can remove the
import registerServiceWorker from "./registerServiceWorker";
from your /client/src/index.js file.
You browser cache may already contain an installed service worker so you may have to
clear browser cache on a user browsers
uninstall the server worker programmatically
import { unregister } from './registerServiceWorker';
....
unregister();
I had the same issues with same symptoms exactly.
For me the cause was a typo in the keys: in server/config/prod.js I had a line reading cookieKey: process.env.COOKIE_KEY but in Heroku Config Variables that variable was named cookieKey. Renaming it to COOKIE_KEY inside Heroku solved the issue.
If you've followed the Stephen Grider tutorial one thing I'm wondering: Is your passport.js file in config or services? I see you've written in index.js: require('./config/passport')
whereas mine in index.js is require('./services/passport')
may not be your solution to the google oauth flow hanging in production but may help.

Resources