I am trying to create a basic game using next.js and node.js. When I run it I get multiple "connected" logs and when I check adapters -using io.sockets.adapter.rooms- I get even though there is just one client (One tab on chrome) connected:
io.sockets.adapter.rooms log:
Map(4) {
'tiTjbdGFj3EGgbwRAAAB' => Set(1) { 'tiTjbdGFj3EGgbwRAAAB' },
'DsCKvImAuh8H6Vr3AAAD' => Set(1) { 'DsCKvImAuh8H6Vr3AAAD' },
'FtJispkyF08rhHBDAAAF' => Set(1) { 'FtJispkyF08rhHBDAAAF' },
'PFO72' => Set(1) { 'FtJispkyF08rhHBDAAAF' }
}
app.js(server backend)
const app = require('express')();
const server = require('http').createServer(app);
const fs = require('fs');
const dirImage = './public/images';
const path = require( 'path' );
const io = require('socket.io')(server, {
cors: {
origin: "http://localhost:3000",
methods: ["GET", "POST"],
}
});
server.listen(8000);
frontend react context socket connection:
import {createContext} from 'react';
import io from "socket.io-client";
export const socket = io("http://localhost:8000", {transports : ['websocket']});
export const SocketContext = createContext(socket);
Also creating socket variable in every page using useContext hook:
const socket = useContext(SocketContext);
When I run the server I still get 5 connections from one client
Connected!
Connected!
Connected!
Connected!
Connected!
What is the problem?
from your frontend app, you are using context api to connect socket but not using useEffect Hooks, So your connection line is running multiple time as how many time your app is re-rendering. I suggest to use useEffect hook with empty dependency array to run the connection only in first render.
I am attaching a sample code for you:
import io from "socket.io-client";
useEffect(() => {
socket = io.connect("http://localhost:8000");
return () => {
socket.disconnect();
};
}, []);
This will connect your socket connection first time and it will disconnect when the component ejects.
Should it be like this?
context file:
const SocketContext = createContext();
const SocketContextProvider = ({children}) =>{
const [socket,setSocket] = useState(null);
useEffect(()=>{
setSocket(io.connect("http://localhost:8000"));
},[]);
return (
// the Provider gives access to the context to its children
<SocketContext.Provider value={socket}>
{children}
</SocketContext.Provider>
);
}
export {SocketContext,SocketContextProvider}
Related
I have a MERN application. I want to run this on Heroku. That also works so far. But I can't get socket.io to run on Heroku. My server listens on port 5555. Below I have listed all possible scenarios that I have already tried without success. What else can I do ?
I specify "ws://localhost:5555" or "http://localhost:5555" whatever it is, it works with a local address.
Thank you very much!
index.js Server
import express from "express";
import http from "http";
import { Server } from "socket.io";
const app = express();
const server = http.createServer(app);
const socketio = new Server(server, { cors: { origin: "*" } });
socketio.on("connect", (socket) => {
socket.on("addUser", (userId) => {
addUser(userId, socket.id);
console.log(users);
});
...
});
app.listen(process.env.PORT || 5050, () => {
verbindugZuMongoDb();
});
server.listen(5555);
App.js
import { io } from "socket.io-client";
useEffect(() => {
setSocket(io("wss://example.herokuapp.com:5555/")); // Also not working
setSocket(io("https://example.herokuapp.com:5555/")); // Also not working
setSocket(io()); // Also not working
setSocket(io(":5555")); // Also not working
setSocket(io("https://example.herokuapp.com/")); // Also not working
}, []);
I am trying to do Spotify Authentication using Client(React) and Server, the logging in works for a second then the page refreshes immediately after logging in and logs the user out. Anyone knows where might be the problem?
Here is my code:
server.js:
require('dotenv').config();
const express = require('express');
const cors = require('cors');
const SpotifyWebApi = require('spotify-web-api-node');
const app = express();
app.use(cors()) // to handle the cross-origin requests
app.use(express.json()); // to parse JSON bodies
const port = process.env.PORT || 8000;
const credentials = {
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: process.env.REDIRECT_URI || "http://localhost:3000"
};
app.post('/refresh', (req, res) => {
const refreshToken = req.body.refreshToken;
// console.log("Hii");
let spotifyApi = new spotifyWebApi({
clientId: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
redirectUri: process.env.REDIRECT_URI,
refreshToken,
});
spotifyApi
.refreshAccessToken()
.then((data) => {
// console.log(data.body);
res.json({
accessToken: data.body.access_token,
expiresIn: data.body.expires_in,
})
})
.catch((err) => {
console.log(err);
res.sendStatus(400);
});
});
app.post('/login', (req,res) => {
// Get the "code" value posted from the client-side and get the user data from the spotify api
let spotifyApi = new spotifyWebApi(credentials)
const code = req.body.code
spotifyApi.authorizationCodeGrant(code).then((data) => {
// Returning the User's Data in the json formate
res.json({
accessToken : data.body.access_token,
refreshToken : data.body.refresh_token,
expiresIn : data.body.expires_in
})
})
.catch((err) => {
console.log(err);
res.sendStatus(400)
})
})
app.listen(port, () => {
console.log(`Example app listening at http://localhost:${port}`)
})
Client side:
useAuth.js:
import React from 'react';
import { useEffect, useState } from 'react';
import axios from "axios"
export default function useAuth(code) {
const [accessToken, setAccessToken] = useState();
const [refreshToken, setRefreshToken] = useState();
const [expiresIn, setExpiresIn] = useState();
useEffect(() => {
axios
.post("/login", {code})
.then((res) => {
window.history.pushState({}, null, "/");
console.log(res.data);
setAccessToken(res.data.accessToken);
setRefreshToken(res.data.refreshToken);
setExpiresIn(res.data.expiresIn);
})
.catch(() => {
window.location = "/";
});
}, [code]);
useEffect(() => {
if (!refreshToken || !expiresIn) {
return;
}
let interval = setInterval(() => {
axios
.post("/refresh", {refreshToken})
.then((res) => {
setAccessToken(res.data.accessToken);
setExpiresIn(res.data.expiresIn);
})
.catch(() => {
window.location = "/";
});
}, (expiresIn - 60) * 1000);
return () => clearInterval(interval)
}, [refreshToken, expiresIn]);
return accessToken;
}
spotifyConfig.js:
const authEndpoint = "https://accounts.spotify.com/authorize";
const redirectUri = "http://localhost:3000";
const clientId = "ea28d4ba34f34b44b59c640052c6e098";
const scopes = [
"streaming",
"playlist-modify-public",
"ugc-image-upload",
"user-read-email",
"user-read-private",
"user-read-currently-playing",
"user-read-recently-played",
"user-read-playback-state",
"user-modify-playback-state"
];
export const loginUrl = `${authEndpoint}?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=${scopes.join(
"%20"
)}&show_dialog=true`;
App.js:
import './App.css';
import Dashboard from './pages/Dashboard';
import Login from './components/Login';
const code = new URLSearchParams(window.location.search).get('code')
function App() {
return (
<div>
{code ? <Dashboard code={code}/> : <Login/>}
</div>
);
}
export default App;
๐๐ฝ this page appears a second then logs out Dashboard.js:
import React, {useEffect} from 'react';
import useAuth from '../useAuth';
import SpotifyWebApi from "spotify-web-api-node";
const spotifyApi = new SpotifyWebApi({
clientId: "ea28d4ba34f34b44b59c640052c6e098",
});
export default function Dashboard({code}) {
const accessToken = useAuth(code);
useEffect(() => {
if (!accessToken) return;
spotifyApi.setAccessToken(accessToken);
spotifyApi.getMe().then(data => {
console.log(data);
})
}, []);
return (
<div>
This is the home page ๐
</div>
)
}
Login.js:
import React from 'react';
import { loginUrl } from '../spotifyConfig';
export default function Login() {
return (
<div>
<a href={loginUrl}>
<button>LOGIN WITH SPOTIFY</button>
</a>
<div className="links">
<p>
โ When joining or creating a Queue, open Spotify to be able to queue up tracks
</p>
</div>
</div>
)
}
I experienced a similar issue. Here are a few steps that helped me to resolve it and many of the subsequent issues I encountered.
Run your IDE's debugger and set break points for your /login request. Also, check whether your environment variables are getting set as you intend (if running VSCode you can learn how to set this up here). In particular, make sure your credentials
clientId: process.env.CLIENT_ID, clientSecret: process.env.CLIENT_SECRET, redirectUri: process.env.REDIRECT_URI
are correct. If your environment variables are not being loaded you won't be able to create a new SpotifyWebApi instance (check out dotenv file is not loading environment variables). Another easy way to check if this is the problem is to hard code your values temporarily.
Test your server /login endpoint independently before running the client to see whether the endpoint is returning 400 or another error when executing requests to Spotify.
Make sure you keep your devtools console open in your browser so you can identify any failed requests you're making to the Spotify API and consider setting devtools to preserve logs in case the page refreshing is deleting them.
If you are running both your client and server from inside VSCode, try instead running them both in new shells outside of VSCode.
I was having a similar issue, having followed this tutorial, and then this YouTube tutorial.
What I realised was that my App component was being rendered twice, which was causing everything to be called twice, including the login endpoint. I was able to verify this using console.log in the endpoint and seeing if the log appeared twice. As the same Spotify code was being used twice in SpotifyWebApi.authorizationCodeGrant, this was what was causing the error.
I was able to trace the issue to the React.StrictMode being enabled, which must have happened when using the create-react-app command. Verify whether the tags appear in your index.js file. For more information, check this StackOverflow answer: My React Component is rendering twice because of Strict Mode
Iยดm making a node.js project in typescript. I want to put my socket.io code in a separate file to organize.
I see in the console that socketHandling.ts is loaded before index.ts, which I find odd.
But the problem is that Typeof server in socketHandling.ts is undefined.
How do I make sure the server variable from index.ts is defined before socketHandling.ts is executed?
index.ts
import * as express from "express";
import * as path from "path";
import * as socketHandle from "./socketHandling";
console.log("index.ts loaded");
export const server = express()
.use(express.static(path.join(__dirname, "../../public")))
.set("views", path.join(__dirname, "../../views"))
.set("view engine", "ejs")
.get("/*", httpGet)
.post("/*", httpPost)
.listen(PORT, () => console.log(`Listening on ${PORT}`));
socketHandle.initSockets();
socketHandling.ts
import { Server } from "socket.io";
import { server } from "./index";
console.log("socketHandling.ts loaded");
console.log(server);
const io = new Server(server);
export function initSockets(): void {
io.on("connection", (socket) => {
console.log(`Client connected ${socket.id}`);
socket.on("disconnect", () => {
console.log(`Client disconnected ${socket.id}`);
});
});
}
Why does it go through socketHandling before the index server is defined?
If index.ts is your entry point, once the program counter reaches line 3 of index.ts, the import will make it start executing socketHandling.ts, which contains another import back to index.ts, which wasn't even done executing yet. So this looks like a circular import maybe you should avoid the situation altogether.
Proposed Solution:
Avoid the circular import by passing in the server from the top into the initSockets method that you import from the library file.
Try the following refactor:
index.ts
import * as express from "express";
import * as path from "path";
import * as socketHandle from "./socketHandling";
console.log("index.ts loaded");
export const server = express()
.use(express.static(path.join(__dirname, "../../public")))
.set("views", path.join(__dirname, "../../views"))
.set("view engine", "ejs")
.get("/*", httpGet)
.post("/*", httpPost)
.listen(PORT, () => console.log(`Listening on ${PORT}`));
socketHandle.initSockets(server);
socketHandling.ts
import { Server } from "socket.io";
console.log("socketHandling.ts loaded");
let io: Server;
export function initSockets(server): void {
if (!io) {
io = new Server(server);
}
io.on("connection", (socket) => {
console.log(`Client connected ${socket.id}`);
socket.on("disconnect", () => {
console.log(`Client disconnected ${socket.id}`);
});
});
}
Typically it wont be sustainable to export stuff from your application entry point for the entire library to use. Usually you see these kinds of variables passed down into methods rather than globally referenced across different files throughout the entire application.
I use gRPC but I have a problem initializing the service in Next.js app.
Goal: Create client service only once in app and use it in getServerSideProps (app doesn't use client-side routing).
For example, we have a service generated with grpc-tools (only available on SSR) and then I just want to initialize it somewhere. At first I thought it can be realized in a custom server.js:
const { credentials } = require('#grpc/grpc-js');
const express = require("express");
const next = require("next");
const { MyserviceClient } = require('./gen/myservice_grpc_pb');
const dev = process.env.NODE_ENV !== "production";
const app = next({ dev });
const handle = app.getRequestHandler();
// Init & Export
exports.myService = new MyserviceClient(
'http://localhost:3000',
credentials.createInsecure(),
);
(async () => {
await app.prepare();
const server = express();
server.get("*", (req, res) => handle(req, res));
server.listen(process.env.PORT, () => {
console.log(`Listening at http://localhost:${process.env.PORT}`);
});
})();
And then use it on the homepage, for example:
import React from 'react';
const { GetSmthRequest } = require('../gen/myservice_pb');
const { myService } = require('../server.js');
const IndexPage = () => (
<div>
<span>My HomePage</span>
</div>
)
const getServerSideProps = async () => {
const request = new GetSmthRequest();
request.setSomeStuff('random');
myService.getStmh(GetSmthRequest, (err, res) => {
//...
})
return {
props: {
}
}
}
export default IndexPage;
But for some reason it's not possible to initialize the client service in the server.js.
Also I tried doing it with next.config.js:
const { credentials } = require('#grpc/grpc-js');
const { MyserviceClient } = require('./gen/myservice_grpc_pb');
module.exports = {
serverRuntimeConfig: {
myService: new MyserviceClient(
'http://localhost:3000',
credentials.createInsecure(),
),
},
};
This solution works, so I can use the service through serverRuntimeConfig, thereby initializing it only once in the entire application, but when I make a request somewhere using getServerSideProps, I get an error:
Request message serialization failure: Expected argument of type ...
Error explanation: (https://stackoverflow.com/a/50845069/9464680)
That error message indicates that message serialization
(transformation of the message object passed to gRPC into binary data)
failed. This generally happens because the message object doesn't
match the expected message type or is otherwise invalid
Does anyone know why I am getting this error?
It's also interesting to see some examples of using Next.js with grpc-node.
For such a case you can use Node.js global
I had asked this question before but here's a simple code for the same.
Im sending the data to Node from angular via websocket but I don't get the emitted event:
I've followed 10+ tutorials and forums, newest from here: https://www.youtube.com/watch?v=66T2A2dvplY&t=317s
service:
import { Injectable } from '#angular/core';
import { Observable } from 'rxjs';
import { io } from 'socket.io-client';
#Injectable({
providedIn: 'root'
})
export class SocketService {
socket = io('ws://localhost:3000'); // adding 'http://localhost:3000' also doesnt work
constructor() {}
listen(eventName: string) {
return new Observable( subscriber => {
this.socket.on(eventName, data => {
subscriber.next(data);
});
});
}
emit(eventName: string, data) {
this.socket.emit(eventName, data);
}
}
from component's ngOnInit(), this is called:
this._socketService.listen('test event').subscribe( data => {
console.log("data from server: ", data);
});
server code of Node:
const app = require('./app');
const port = process.env.PORT || 3000;
const server = require('http').createServer(app);
const socketio = require('socket.io');
const io = socketio(server,
{
serveClient: true,
pingInterval: 60000,
pingTimeout: 60000000,
cors: {
origin: "http://localhost:4200",
methods: ["GET", "POST"],
credentials: true
}
});
io.on('connection', function (socket) {
console.log("A user connected ", socket.connected); // works till here for every ping interval
socket.emit('test event', 'here is some data'); // this is either not executed or doesn't work
});
server.listen(port);
socket.connected is always true in NODE server, but false in Angular
What Ive tried:
CORS has been suppressed, I hope that's not the issue cuz nothing is seen like so
changing io.('connection' ...) to io.connect('connect'...)
init this.socket = io(...) in constructor
There is no data exchange seen in Network or Network > WS tab in case I emit from Angular too
This is my 3rd day with this problem, I'll highly appreciate any help.
Thank you
your mixing protocols from client.
change
socket = io('ws://localhost:3000');
to
socket = io('http://localhost:3000', { withCredentials: true });
As mentioned in introduction under chapter What Socket.IO is not that socket.io is not a pure WS lib.