I'm trying to get a node.js server (using express) working using websockets in elastic beanstalk (EB) using application load balancer (ALB) but without using socket.io (because peerjs-server is the server I'm trying to get running and it's not written with socket.io).
I've seen a couple of articles suggesting you have to use socket.io (or another lib that doesn't just rely on websockets), but Amazon says ALB supports websockets directly.
My server is both a create-react-app server and a peerjs-server. It runs fine in dev on port 9000 for both the web UI and the peerjs ws connections.
I've tried all of the different approaches I've found, but I haven't gotten this to work, and I've even seen things written that suggest it can't be done, but it seems like I'm close. Has anyone gotten this to work, and if so, how?
Okay, I got it to work. Here's what I've done to get everything working on port 9000.
In EB, create an application, then begin creating an environment.
In the configuration of the environment, go into the Software section and tell it that you're going to use npm run prod
to start your server.:
Now, go into the Load Balancers section and, as shown in the pic below:
You may need to hop over to the EC2 dashboard in the AWS UI in order to make sure that the necessary Security Groups are defined there. I think I created the first two on the and the last two were default creations, but I don't recall. Anyway, they need to have port 9000 open for inbound and outbound (port 80 is there by default all the time):
Back to the EB configuration, go to the Instances section and make sure that your instance has the Security Groups assigned to it:
I ran react-scripts build
to make the /build
directory containing the production version of the server UI(this is create-react-apps stuff that I'm not covering here).
In my code, I start the server using a server.js
that makes a server that runs both the peerjs-server ws server and an http server.
const express = require("express");
const bodyParser = require("body-parser");
const path = require('path');
const passport = require("passport");
const users = require("./routes/api/users");
const games = require("./routes/api/games");
const Game = require("./src/models/Game");
const WebSocket = require('ws');
const ExpressPeerServer = require('peerjs-server').ExpressPeerServer;
const app = express();
const url = require('url');
const port = process.env.PEERSERVERPORT || 9000;
// WebSocket for making db updates to client.
const wsserver = require('./wsserver').wsserver
// Server that gets all requests: lobby UI, peerserver, db websocket
const server = require('http').createServer(app);
wsserver.on('connection', function connection(ws) {
ws.on('message', msg => {
ws.send(msg)
})
ws.on('close', () => {
console.log('WebSocket was closed')
})
});
server.on('upgrade', function upgrade(request, socket, head) {
const pathname = url.parse(request.url).pathname;
if (pathname === '/ws') {
wsserver.handleUpgrade(request, socket, head, function done(ws) {
wsserver.emit('connection', ws, request);
});
}
});
// Bodyparser middleware
app.use(
bodyParser.urlencoded({
extended: false
})
);
app.use(bodyParser.json());
// Passport middleware
app.use(passport.initialize());
// Passport config
require("./config/passport")(passport);
// Routes -- the /api/* path is defined in the EB load balancer
app.use("/api/users", users);
app.use("/api/games", games);
// This is the create-react-apps /build directory of UI code
app.use(express.static(path.join(__dirname, 'build')));
app.get('/login', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
// These are the paths that are defined in the EB load balancer
app.get('/logout', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.get('/register', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
app.get('/dashboard', function (req, res) {
res.sendFile(path.join(__dirname, 'build', 'index.html'));
});
// Peer server for making WebRTC connections between game clients.
const options = {
debug: true
}
const peerserver = ExpressPeerServer(server, options);
app.use('/peerserver', peerserver);
app.use(express.static(path.join(__dirname, 'src')));
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname, 'src', 'index.html'));
});
peerserver.on('disconnect', (client) => {
console.log('Delete the game if the host client disconnects');
Game.deleteMany({ hostPeerId: client })
.then(() => {
wsserver.clients.forEach(function each(client) {
if (client.readyState === WebSocket.OPEN) {
client.send("refreshGames");
}
})
})
.catch(err => console.log(err));
});
server.listen( port, () => console.log(`Server is up and running on port ${port} !`))
And then in my package.json
, I set the script for prod to run the above server.js to start the server, and the npm run prod I put in the Software section earlier is what calls this to make the server go:
...
"scripts": {
"start": "set PORT=8081&& react-scripts start",
"prod": "node server.js",
...
After doing all that, I now have a running EB server using ALB and handling both websocket (ws) and UI (http) traffic on port 9000.