Cross import in node.js - javascript

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.

Related

vitest Router.use() requires a middleware function but got a undefined

I am trying to unit test my router object in Express but inside the unit test file the object returns undefined
Here is a minimal version of my app
src/config/apiVersion.js
// update major versions here
const version = '/v2'
export default version
src/routes/index.js
import express from 'express'
import {
healthRouter,
healthUrl
} from './health/index.js'
const router = express.Router()
// add new routes here
const allRoutes = [
{
path: healthUrl,
route: healthRouter
}
]
// tell the router to use the routes you added
allRoutes.forEach((route) => {
router.use(route.path, route.route)
})
export default router
src/routes/health/index.js
import express from 'express'
import { healthController } from '../../controllers/health/index.js'
const healthRouter = express.Router()
const healthUrl = '/health'
healthRouter.route('/')
.get(healthController)
export {
healthRouter,
healthUrl
}
src/app.js (note I omitted most of the app.use's such as app.us(cors()) for example
// version is just the string '/v2'
import version from './config/apiVersion.js'
import router from './routes/index.js'
const app = express()
// some other app.use's here omitted like app.use(cors)
// add routes
app.use(`${version}`, router)
// custom 404 to handle non-existent paths/typos on paths
app.use((req, res) => {
res.status(404).send({ error: 'Path does not exist, check for typos. If querying /soap you also need vendor and method in the path' })
})
// custom error handler
app.use((err, req, res) => {
appLogger.error('There was an error: ' + err.stack)
res.status(500).send('Something broke!')
})
export default app
Here is my test file
import router from '../../../src/routes/index.js'
// to make sure the number of routes doesn't change without a new test added
const actualNumberRoutes = 2
describe('router', () => {
it('should return all the routes', () => {
let numberOfRoutes = 0
router.stack.forEach((layer) => {
expect(layer.name).toEqual('router')
numberOfRoutes += 1
})
expect(numberOfRoutes).toEqual(actualNumberRoutes)
})
})
And the error for this file where router is coming up as undefined
Try to provide your app to use routes after importing your respective route files like this.
import healthRouter from 'src/routes/health/index.js';
import router from '../../../src/routes/index.js';
const app=express();
app.use("your_path",router);
app.use("your_health_path",healthRouter);

Socket.io on Heroku does not run as expected

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
}, []);

socket io server connection fires multiple times

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}

SSR with React : Unexpected token '<' in call to renderToString()

I'm working on SSR with react but I'm encountering the following error.
Syntax error: Unexpected token '<'`
<div id="root">${ReactDOMServer.renderToString(<App />)}</div>```
^
As mentioned is here
babel-register doesn't process the file it is called from.
Therefore, I rightly declared my babel dependencies in a new file, however I'm still getting the above error.
Below is my index.js file
import babelRegister from '#babel/register';
import ignoreStyles from 'ignore-styles';
babelRegister({
ignore: [/node_modules/],
presets: ['#babel/preset-env', '#babel/preset-react'],
});
import express from 'express';
import appRender from './server.js';
const app = express();
appRender(app);
My server.js file.
import initialRenderRoutes from './routes/initialRenderRoutes.js';
import path from 'path';
const appRender = (app) => {
const __dirname = path.resolve();
app.use(express.static(path.resolve(__dirname, '../build')));
app.use('*', initialRenderRoutes);
const port = 5000;
app.listen(port, () => console.log(`Server running on port ${port}`));
};
export default appRender;
My initialController.js file
import fs from 'fs';
import ReactDOMServer from 'react-dom/server.js';
import path from 'path';
import App from '../../src/App.js';
const initialRenderController = (req, res, next) => {
console.log(path.resolve());
fs.readFile(
path.resolve('../client/build/index.html'),
'utf8',
(err, data) => {
if (err) {
console.log(err);
return res.status(500).send('Internal Server Error');
}
return res.send(
data.replace(
'<div id="root"></div>',
`<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`
<<<<The problem lies here>>>>
)
);
}
);
};
export default initialRenderController;
Is it something related to babel, please help.
Try the below changes in your index.js file,
require('ignore-styles');
require('#babel/register')({
ignore: [/(node_modules)/],
presets: ['#babel/preset-env', '#babel/preset-react']
});
require('./server');
require('./initialController');
The above should work, I tested locally the below, it works perfectly fine.
My server.js
import express from 'express';
import fs from 'fs';
import path from 'path';
import React from 'react';
import ReactDOMServer from 'react-dom/server';
import App from '../App';
const app = express();
app.use('^/$', (req, res, next) => {
fs.readFile(path.resolve('./build/index.html'), 'utf-8', (err, data) => {
if (err) {
console.log(err);
return res.status(500).send("Some error occurred")
}
return res.send(data.replace('<div id="root"></div>', `<div id="root">${ReactDOMServer.renderToString(<App />)}</div>`))
})
});
app.use(express.static(path.resolve(__dirname, "..", "build")));
app.listen(5000, ()=>{
console.log("App running on port 5k")
})
index.js
require('ignore-styles');
require('#babel/register')({
ignore: [/(node_modules)/],
presets: ['#babel/preset-env', '#babel/preset-react']
});
require('./server');
I hope you have the .babelrc file with the required presets.
Update in response to comment:
Consider removing type: "module", since it will throw error when you use require. #babel/register will run files using babel on the fly. The require hook will bind itself to the node’s require and will automatically compile files at runtime. server.js using es module won't clash if you remove type: "module". The order of require matters, we require babel-register in index.js with the presets needed to recognize the syntaxes in the then-required server.js.
I believe there are two things that need to be changed. One on your initialControler.js you are using export default in a node.js file, use module.exports
module.exports vs. export default in Node.js and ES6
You should change all the imports in your node files.
You use export / export default in React and then import to pull in the files
https://www.geeksforgeeks.org/reactjs-importing-exporting/
module.exports and require to pull in the files for Node
What is the purpose of Node.js module.exports and how do you use it?
Second they moved the app.get into that renderReact.js file and then required it into their index.js file. However on your server.js file I don't see you importing in your initialController file.
From your example it looks like you should be doing something like this:
Server.js
let initialController = require('/path to this file');
initialController(app)
Yow broh don’t waste yow time reading them parchments.
All you need to do is remove any space b4 and after each ><
const val= ReactDOMServer.renderToString(<App />);
/// make sure you don’t have any sort of space
/// between them > < and yow ${}
/// is better if you store that long text into an small var, as I did to ///prevent prettier or some other 💩 to add a line break of an space
return res.send( `<div id="root">${val}</div>`);

get request throwing 404 error in Angular 5 app

I am a newbie to Angular.I am creating a demo video player app, following youtube series, but I am struck at a point where I can't get data for my get request.I am using Angular 5.2.10.Below are my files and code:
server.js:
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const api = require('./server/routes/api');
const port = 3000;
const app = express();
app.use(express.static(path.join(__dirname,'dist')));
app.use(bodyParser.urlencoded({extended:true}));
app.use(bodyParser.json());
app.use('/api',api);
app.get('*',(req,res)=>{
res.sendFile(path.join(__dirname,'dist/index.html'));
});
app.listen(port,function(){
console.log("server running on localhost"+port);
});
api.js:
const express = require('express');
const router = express.Router();
const mongoose = require('mongoose');
const Video = require('../models/video');
const
db="mongodb://usersubhash:subhashpwd#ds217350.mlab.com:17350/videoplayer";
mongoose.Promise = global.Promise;
mongoose.connect(db,function(err){
if(err){
console.log("Error!"+err);
}
});
router.get('/videos',function(req,res){
//res.send('api works');
Video.find({}).exec(function(err,videos){
if(err){
console.log("error retrieving videos");
}else{
res.json(videos);
}
});
});
module.exports = router;
video.js:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const videoSchema = new Schema({
title:String,
url:String,
description:String
});
module.exports = mongoose.model('video',videoSchema,'videos');
video.ts:
export class Video {
_id:string;
title:string;
url:string;
description:string
}
environment.ts
export const environment = {
production: false,
apiUrl: 'http://localhost:3000'
};
video.service.ts:(where I have getVideos() method)
import { Injectable } from '#angular/core';
import {Http,Response} from '#angular/http';
import 'rxjs/add/operator/map';
#Injectable()
export class VideoService {
constructor(private _http:Http) { }
private _getUrl = `${environment.apiUrl}/api/videos`;
getVideos(){
return this._http.get(this._getUrl).map((response:Response)=> response.json());
}
}
videoCenter.component.ts:(where I am subscribing to getVideos() method):
import { Component, OnInit } from '#angular/core';
import {Video} from '../video';
import { VideoService } from '../video.service';
#Component({
selector: 'app-video-center',
templateUrl: './video-center.component.html',
styleUrls: ['./video-center.component.css'],
providers:[VideoService]//,Http,HttpClientModule
})
export class VideoCenterComponent implements OnInit {
myAllVideos:Array;//here I want to put my get Request Data
constructor(private _videoService:VideoService) { }
selectedVideo:Video;
onSelectVideo (video:any){
this.selectedVideo=video;
}
ngOnInit() {
this._videoService.getVideos().subscribe(result => this.myAllVideos = result);
}
}
When I run node server.js in VSCode terminal , then in POSTMAN app I can get all records by requesting GET in "localhost:3000/api/videos".But in my app, I am unable to load data which is running in 4200 port.
When I click on button which loads video-center.component.ts , getVideos() is triggered in ngOnInit() but it throws this error:
Your screenshot showing the error has this url:
http://localhost:4200/api/videos
But your server.js says:
const port = 3000;
So your server is running on port 3000, not 4200. Port 4200 is normally where Angular runs.
So you need to modify your getUrl:
private _getUrl = "http://localhost:3000/api/videos";
Rather than hard code this I suggest you read-up on how to setup an environment file, and put the host part "http://localhost:3000" in the environment file and read it from there. Then your code could be:
private _getUrl = `${environment.apiUrl}/api/videos`;
NOTE
Just to be clear - although Angular runs on the client, it is an app that has to be started from somewhere. For example, in a production situation you might have this:
https://app.mydomain.com <- users visit this, and the browser starts running your angular app
https://api.mydomain.com <- your angular app will get its data from here
In production it's quite likely both of these urls will be accessed on port 80. But since the subdomains are different (api versus app) that is perfectly fine.
However, when running locally in development mode, you cannot run two different things (ie. an Angular application and a Node application) on the same address (localhost) with the same port.
Since you are running them both on localhost, they must have different ports. So when you wrote:
return this._http.get(this._getUrl)...
it is defaulting to where Angular itself is running, localhost:4200, not your api. You need to tell angular your api is on port 3000.

Categories