i am been struggling for days, on this problem.
I am making an Universal React App with webpack 5.
I have enabled HMR on the client side by webpack-dev-middleware and webpack-hot-middleware
Now i want to do the same things for the backend witch is an express server.
I have tried nodemon but my biggest complain is that does auto restart the server and it doesn't match well with webpack-hot-middleware.
Then I have found webpack-hot-server-middleware witch just what i am looking for but it's not working and it is no longer maintained.
It is stuck in an infinite loop because of webpack-dev-middleware.
I have found this amazing github example linked by webpack-hot-middleware himseld on the troubleshooting section.
But again it is not quite working.
The longer it stay on this problem, I realise how much complex I try to accomplish.
Because I am on a webpack project where there two configuration (development and production) and each one does build theirs bundles.
I launch my node server on the server bundle and I want this to reload when I change the source code witch is in a another directory.
webpack-dev-middleware and webpack-hot-middleware does the job on the frontend because they both use the webpack configurations as a parameter but on backend it is much harder.
Here is what I have done so far:
Here is my main express server file:
server.js
import express from 'express';
import path from 'path';
import cors from 'cors';
import routerPage from './renderpage/routerPage.js';
import {envIsProduction, envIsDevelopment} from './envmode/envUtil.js';
import chokidar from 'chokidar';
const PORT = process.env.PORT || 8080;
let server = express();
if (envIsProduction()) {
console.log(" _______________________________________ ");
console.log("| |");
console.log("| ( PRODUCTION ) |");
console.log("|_______________________________________|");
server.use(express.static(path.join(__dirname,'..'))); // we serve static file like the bundle-app.js to the browser from the current directory where the server is executed and we move to the top root to access the file
}
else if (envIsDevelopment()) {
console.log(" _______________________________________ ");
console.log("| |");
console.log("| ( DEVELOPMENT ) |");
console.log("|_______________________________________|");
// TODO: Régler cohabitation webpack-dev-middleware qui rafraichit en boucle
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const webpackHotServerMiddleware = require('webpack-hot-server-middleware');
const config = require('../../webpack.conf.dev.js');
const compiler = webpack(config);
server.use(webpackDevMiddleware(compiler, {
serverSideRender: true,
}));
server.use(webpackHotMiddleware(compiler.compilers.find(compiler => compiler.name === 'client')));
// Do "hot-reloading" of express stuff on the server
// Throw away cached modules and re-require next time
// Ensure there's no important state in there!
const watcher = chokidar.watch('.');
watcher.on('ready', function() {
watcher.on('all', function() {
console.log("OOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOO Clearing /server/ module cache from server");
Object.keys(require.cache).forEach(function(id) {
console.log(id)
console.log("exist: " + /.[\/\\]/.test(id));
if (/.[\/\\]/.test(id)) delete require.cache[id];
});
});
});
// Do "hot-reloading" of react stuff on the server
// Throw away the cached client modules and let them be re-required next time
compiler.hooks.done.tap('done', (stats) => {
console.log("Clearing /client/ module cache from server");
Object.keys(require.cache).forEach(function(id) {
if (/[\/\\]client[\/\\]/.test(id)) delete require.cache[id];
});
})
}
server.use(cors());
server.use(express.urlencoded({extended:false}));
server.use(express.json());
server.get('/', routerPage); // when the user connect to the root of the server we send the page
server.get('/click', (req, res)=>{
console.log("IUHpzeijgàzhgzieghziêhg");
res.send("I got ur six");
});
server.get("*",function(req,res){
res.status(404).send('page not found');
}); //For all other type of request excluding the one specified here, we send back a 404 page;
server.listen(PORT, () => {
console.log(
`Server listening on \x1b[42m\x1b[1mhttp://localhost:${PORT}\x1b[0m in \x1b[41m${process.env.NODE_ENV}\x1b[0m`,
);
});
I use chokidar to watch all the file on the current directory including the file where the code is and I clear the cache to refresh the server.
Here is the tree structure of my project:
As you can see, I launch node on the bundle-server.js file but I want reload to occur when I change all the file in the serverside directory
But even the console appear, nothing change when I check.
Do you have any advices?
How can I make HMR work on the server side?
Thanks in advance for your responses.
Related
Hello I am a bit confused by this error I have encountered.
I am working on an Universal React App using Webpack 5 and Express.
I want to implement Jest support by using the React-testing-Library for the frontend (which work) and supertest for the backend (this is where I am blocked).
I am following this basic tutorial recommended by the jest doc himself in order to use jest on an node express environment.
But everytime I get this error:
thrown: "Exceeded timeout of 5000 ms for a test.
Use jest.setTimeout(newTimeout) to increase the timeout value, if this is a long-running test."
Here are my code:
server.js
import app from './app.js';
import { mongooseConnection, disconnectMongoDB } from "./routers/services/url/urlDB.js"; // we call mongooseConnect var to connect only once into the mongoDB database
const PORT = process.env.PORT || 8080;
// the server listen on the port set by node on localhost.
app.listen(PORT, () => {
console.log(
`Server listening on \x1b[42m\x1b[1mhttp://localhost:${PORT}\x1b[0m in \x1b[41m${process.env.NODE_ENV}\x1b[0m`,
);
});
// when when we shut down the app we execute a callback function before closing the server
process.on('exit', function() {
disconnectMongoDB();
});
app.js
import express from 'express';
import path from 'path';
import cors from 'cors';
import {envIsProduction, envIsDevelopment} from './envmode/envUtil.js';
import { enableHMR } from './reload/hotReload.js';
let app = express();
// if we have set the environnent on production then:
if (envIsProduction()) {
console.log(" _______________________________________ ");
console.log("| |");
console.log("| ( PRODUCTION ) |");
console.log("|_______________________________________|");
console.log(" ");
app.use(express.static(path.join(__dirname,'../client'))); // we serve static file like the bundle-app.js to the browser from the current directory where the server is executed and we move to the top root to access the file
}
else if (envIsDevelopment()) {
console.log(" _______________________________________ ");
console.log("| |");
console.log("| ( DEVELOPMENT ) |");
console.log("|_______________________________________|");
console.log(" ");
enableHMR(app); // we enable the Hot MPodule Reload on the frontend and the backend
}
app.use(cors());
app.use(express.urlencoded({extended:false}));
app.use(express.json());
//Hot reload!
//ALL server routes are in this module!
app.use((req, res, next) => {
require("./routers/routers")(req, res, next);
});
export default app;
routers.js
import renderPage from "./renderpage/renderPage.js";
import { serverRoutes, reactRouterRoutes, getReactRouterRoutesString } from "./routes.js";
import express from "express";
import routerLoginDB from "./request/routerLoginDB.js";
import routerSignupDB from "./request/routerSignupDB.js";
const router = express.Router();
// Put all your server routes in here
// When the user connect to the root of the server we send the page
router.get(serverRoutes.root, renderPage);
// When the user send a get request by the /click route a console.log and a respone is send.
router.get(serverRoutes.click, (req, res)=>{
res.status(200).send("Click");
});
// when this user want to login into his account, we ask for the routerLoginDB to handle it
router.post(serverRoutes.login,routerLoginDB);
// when this user want to signup into his account, we ask for the routerSignupDB to handle it
router.post(serverRoutes.signup, routerSignupDB);
// For all the routes that only react-router need to use, if we refresh on a nested route of the react-router from the client side then we redirect it to the root route "/"
router.get(reactRouterRoutes,(req,res) => {
res.redirect("/");
});
router.get("*", (req,res) =>{
res.status(404).send('page not found');
}); //For all other type of request excluding the one specified here, we send back a 404 page;
module.exports = router;
app.test.js
import request from '../utils/test-node-utils.js'
describe("Test the /click path", () => {
test("It should response the GET method", () => {
return request
.get("/click")
.expect(200);
});
});
and finally test-node-utils.js
import supertest from "supertest";
import app from "../serverside/app.js";
const request = supertest(app);
export default request;
Don't believe what the error say because I think it is more deep than that.
I have tried to increased the jest timeout value but it keep being stuck and reach the timeout limit.
I have done exactly like the tutorial say without using my project structure and it worked but when I try to implement the tutorial in my backend structure, it don't work with supertest.
I think it is related to my files or backend structure that make it don't work with the test.
Thanks in advance for your help
I've recently debugged a similar issue where my Jest tests would run successfully (or not) in my dev. environment but when I would try and package the app as a Docker image all my tests would time out.
It turned out that by commenting out the line which setup CORS, which for me I only turned on in production builds (should have been a clue), the tests started to run again when building the image.
...
const NODE_ENV = process.env.NODE_ENV;
const app = express();
NODE_ENV.toUpperCase() === 'PRODUCTION' && app.use(cors);
...
I mentioned this as I can see from your snippet above that you are also using the cors middleware and that, in your case, it's set all the time.
Perhaps not your issue, and you may want CORS in your tests for some reason, but try commenting it out and see if your tests run.
I have front-end folder files with react components and front-end css libraries.
In different folder, I have back-end files with server.js routing with mysql connection.
When I enter inputs on the screen, the inputs are not saved to mysql database.
Questions:
In what file, do I connect my front-end with back-end?
What statement should I use to connect my front-end with back-end?
To start Front-end, I used: npm start and To start Back-end, I used: nodemon server.js.
Question: When I connect front-end and back-end, what file should I open so that the front-end talks with the back-end -> both are starting?
Question 1 answer
You can do this a number of ways. I've done this with Serverjs and react in the
following manner.
const express = require('express');
const bodyParser = require('body-parser');
const cors = require('cors');
const port = 80085;
const app = express();
const token = 'impossiblylongimportanttokenhere';
let nextId = 7;
You'll need to import CORS, a Parser, Express for routing purposes.
Assuming you'll have a login you'll want a authenticator function. Please note this is just for development.
function authenticator(req, res, next) {
const { authorization } = req.headers;
if (authorization === token) {
next();
} else {
res.status(403).json({ error: 'User must be logged in to do that.' });
}
}
You'll probably want to replace that with something more secure once you're don with development.
with app as our router express has several CORS methods to get us started likepostget
Here's an example of one if we had an array called friends and if we wanted to find that friend by ID.
app.get('/api/friends/:id', authenticator, (req, res) => {
const friend = friends.find(f => f.id == req.params.id);
if (friend) {
res.status(200).json(friend);
} else {
res.status(404).send({ msg: 'Friend not found' });
}
});
The best part is that express has a method called listen that will start as soon as we hit NPM RUN on a cli that's parked in the same file location as server.js. Make sure to specify a port that your system isn't already using
app.listen(port, () => {
console.log(`server listening on port ${port}`);
});
Question 2
In order to get connect to Server.js on our side you'll want use axios to make a GET/POST etc. call to whatever route you've made in your server.js above in the example it would be
.post('/api/friends', this.state.addFriend)
The biggest thing is you'll want multiple terminals running in order to have both the backend and the front end running at the same time. Start with backend first.
I'm using indexeddb in an Angular 8 service and need window. The code builds without errors and the app creates the db objectstore flawlessly. But at runtime in production mode (with an actual node server instead of ng serve where this error does not occur), I get this error in the terminal running angular:
ERROR ReferenceError: window is not defined
at IndexedDBService.isSupported (D:\MartijnFiles\Documents\Programming\Fenego\fenego-labs-angular\dist\server.js:71199:9)
at IndexedDBService.openDB (D:\MartijnFiles\Documents\Programming\Fenego\fenego-labs-angular\dist\server.js:71203:18)
at Promise (D:\MartijnFiles\Documents\Programming\Fenego\fenego-labs-angular\dist\server.js:72026:46)
Again, it all works and the isSupported() function would stop openDB() from being run if window was actually undefined. There is also no error in the browser console.
Here is the relevant part of my service.
#Injectable()
export class IndexedDBService {
isSupported(): boolean {
return !!window.indexedDB;
}
openDB(dbName: string,
version: number,
onUpgradeNeededCallback: OnUpgradeNeededCallback,
onSuccessCallback: OnOpenSuccessCallback,
onErrorCallback: OnOpenErrorCallback,
onBlockedCallback: OnOpenBlockedCallback): Observable<IDBOpenDBRequest> {
let openDBRequest: IDBOpenDBRequest = null;
if (this.isSupported()) {
openDBRequest = window.indexedDB.open(dbName, version);
openDBRequest.onupgradeneeded = onUpgradeNeededCallback;
openDBRequest.onsuccess = onSuccessCallback;
openDBRequest.onerror = onErrorCallback;
openDBRequest.onblocked = onBlockedCallback;
}
return of(openDBRequest);
}
There are many suggest "solutions" out there that mostly boil down to providing it via a service or plain injection (eg. point 1 in this blog https://willtaylor.blog/angular-universal-gotchas/) but all it does is pass window from some other service via injection to mine. But my code works so it clearly has access to window...
Update:
The following line in a component's ngOnInit() has the same problem with Worker being "not defined" yet the worker is loaded and runs perfectly:
const offlineProductsWorker = new Worker('webworkers/offline-products-worker.js');
Update2:
I have found a solution (posted below) but checking for server side rendering seems more like a workaround than solving the fact that server side rendering is happening (not sure if that is supposed to be the case).
I will include my server.ts script that I use with webpack below. It is a modification of one from another project and I don't understand most of it. If anyone can point out to me what I could change to stop the server side rendering, that would be great. Or, if it is supposed to do that then why?
// tslint:disable:ish-ordered-imports no-console
import 'reflect-metadata';
import 'zone.js/dist/zone-node';
import { enableProdMode } from '#angular/core';
import * as express from 'express';
import { join } from 'path';
import * as https from 'https';
import * as fs from 'fs';
/*
* Load config from .env file
*/
require('dotenv').config({ path: './ng-exp/.env' });
const IS_HTTPS = process.env.IS_HTTPS === 'true';
const SSL_PATH = process.env.SSL_PATH;
const ENVIRONMENT = process.env.ENVIRONMENT;
// Faster server renders w/ Prod mode (dev mode never needed)
enableProdMode();
const logging = !!process.env.LOGGING;
// Express server
const app = express();
const PORT = process.env.PORT || 4200;
const DIST_FOLDER = process.cwd();
// * NOTE :: leave this as require() since this file is built Dynamically from webpack
const { AppServerModuleNgFactory, LAZY_MODULE_MAP } = require('./dist/server/main');
// Express Engine
import { ngExpressEngine } from '#nguniversal/express-engine';
// Import module map for lazy loading
import { provideModuleMap } from '#nguniversal/module-map-ngfactory-loader';
// Our Universal express-engine (found # https://github.com/angular/universal/tree/master/modules/express-engine)
app.engine(
'html',
ngExpressEngine({
bootstrap: AppServerModuleNgFactory,
providers: [provideModuleMap(LAZY_MODULE_MAP)],
})
);
app.set('view engine', 'html');
app.set('views', join(DIST_FOLDER, 'ng-exp'));
// Server static files from /browser
app.get(
'*.*',
express.static(join(DIST_FOLDER, 'ng-exp'), {
setHeaders: (res, path) => {
if (/\.[0-9a-f]{20,}\./.test(path)) {
// file was output-hashed -> 1y
res.set('Cache-Control', 'public, max-age=31557600');
} else {
// file should be re-checked more frequently -> 5m
res.set('Cache-Control', 'public, max-age=300');
}
},
})
);
// ALl regular routes use the Universal engine
app.get('*', (req: express.Request, res: express.Response) => {
if (logging) {
console.log(`GET ${req.url}`);
}
res.render(
'index',
{
req,
res,
},
(err: Error, html: string) => {
res.status(html ? res.statusCode : 500).send(html || err.message);
if (logging) {
console.log(`RES ${res.statusCode} ${req.url}`);
if (err) {
console.log(err);
}
}
}
);
});
const sslOptions = {
key: fs.readFileSync(`${SSL_PATH}/${ENVIRONMENT}/server.key`),
cert: fs.readFileSync(`${SSL_PATH}/${ENVIRONMENT}/server.crt`),
};
// Start up the Node server
let server;
if (IS_HTTPS) {
server = https.createServer(sslOptions, app);
} else {
server = app;
}
server.listen(PORT, () => {
console.log(`Node Express server listening on http${IS_HTTPS ? 's' : ''}://localhost:${PORT}`);
const icmBaseUrl = process.env.ICM_BASE_URL;
if (icmBaseUrl) {
console.log('ICM_BASE_URL is', icmBaseUrl);
}
});
There is a related issue here:
https://github.com/hellosign/hellosign-embedded/issues/107
Basically, to avoid the error you can declare somewhere globally the window.
if (typeof window === 'undefined') {
global.window = {}
}
I also found React JS Server side issue - window not found which explains the issue better and why it works on the client side.
I found the solution thanks to some input from ChrisY
I deploy my code using webpack and run it using node. It seems that node somehow renders it server side and then the browser renders it too. The server site portion has no effect on the storefront but does cause the (seemingly harmless) error. In isSupported() I added console.log(isPlatformBrowser(this.platformId))and it printed false in the server terminal but true in the browser. Thus, I changed the code as follows:
constructor(#Inject(PLATFORM_ID) private platformId: any) {}
isSupported(): boolean {
return isPlatformBrowser(this.platformId) && !!indexedDB;
}
Now it still works in the browser as it did before but there is no server error.
Update:
I have also found the cause for the server side rendering. The server.ts file in the description has a block with res.render(. This first renders the page on the server and if it does not receive html, it returns status code 500. Otherwise it allows the client to render it. Seeing as this is a realistic scenario, I have decided to keep the extra isPlatformBrowser(this.platformId) check in my code. This should then be done for anything that can only be performed by the client (window, dom, workers, etc.).
Not not have server side rendering, an alternative to the res.render( block is
res.status(200).sendFile(`/`, {root: join(DIST_FOLDER, 'ng-exp')});
I created a basic react app with Create React App. I removed all default files and added this into index file.
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
class App extends Component {
constructor() {
super();
this.state = {};
}
async componentDidMount() {
const response = await fetch('/express_backend');
const json = await response.json();
this.setState({ data: json.express });
}
render(){
return (
<div>{this.state.data}</div>
);
}
}
ReactDOM.render(<App/>, document.getElementById('root'));
This app works. I can add some letters between divs and it reloads when saving. But when I add simple Express server the reloading stops. I also add "proxy": "http://localhost:5000/" into package.json inside CRA to connect server and client
Here is the server
const express = require('express');
const app = express();
const port = 5000;
app.listen(port, () => console.log(`Listening on port ${port}`));
// create a GET route
app.get('/express_backend', (req, res) => {
res.send({ express: 'YOUR EXPRESS BACKEND IS CONNECTED TO REACT' });
});
I restarted both Create react app and server in console but now when I add smth between divs in the component nothing is reloading. I guess webpack dev server starts working improperly.
How can I make reloading in CRA work and make request to express each time when I change smth in component?
When running create-react-app with express, you'll need to run your server.js express app in a separate node process than the react-js-app. Also, you'll need to specify the proxy in your package.json.
Utilize these commands in separate terminals:
node server.js
and in separate terminal:
npm start
Kudos to this tutorial: https://dev.to/loujaybee/using-create-react-app-with-express
Here is how my app.js looks like...
I want to know how do I separate this single file into multiple files for better code quality. How do we do that?
My idea is to have the below files...
server.js -> for server related properties
serve.js -> this becomes the main file in the package.json
apis -> this shall be the file with apis, I shd be able to separate apis depending upon the modules too...
What is the preferred convention in NodeJS? I am sure that we do not want to write all apis in one single file.
const express = require('express') // import express module
const app = express() // initiate express app
app.use(express.json()) // not sure what this is but without it POST cant read the JSON parameters from the body
const host = 'localhost' // host
const port = process.env.PORT || 1338 // pick port
const routePrefix = '/' + 'api' + '/' // this is the route prefix used from where the APIs will be accesssed
const routes = { // define routes
root: routePrefix + 'root',
test: routePrefix + 'test',
items: routePrefix + 'items',
item: routePrefix + 'items/:id'
}
// print details
function printDetails(currentRoute, requestMethod, requestParams,
requestQuetyString) {
console.log(currentRoute, requestMethod, requestParams, requestQuetyString);
}
// get root
app.get(routes.root, (req, res) => {
printDetails(routes.root, req.method, req.params, req.query)
res.send(routes.root)
})
// get test route
app.get(routes.test, (req, res) => {
printDetails(routes.test, req.method, req.params, req.query)
res.send(routes.test)
})
// for the web server
app.use(express.static('../public')) // this is where static files reside and need to be served to for the clientside app
// start the API server and Web server
app.listen(port, () => {
console.log(`
\nExpress Server started on port ${port}..
APIs can be accessed at http://${host}:${port}${routePrefix}
Web Server started on port http://${host}:${port}
`)
})
I have tried it on my own and the respective files look like this. However, I am unable to run this.
server.js
const express = require('express') // import express module
const app = express() // initiate express app
app.use(express.json()) // not sure what this is but without it POST cant read the JSON parameters from the body
//const api = require('./apis')
//const app = api.app
const host = 'localhost' // host
const port = process.env.PORT || 1338 // pick port
const routePrefix = '/' + 'api' + '/' // this is the route prefix used from where the APIs will be accesssed
const routes = { // define routes
root: routePrefix + 'root',
test: routePrefix + 'test',
items: routePrefix + 'items',
item: routePrefix + 'items/:id'
}
// for the web server
app.use(express.static('../public')) // this is where static files reside and need to be served to for the clientside app
module.exports = {
app: app,
host: host,
port: port,
routePrefix: routePrefix,
routes: routes
}
serve.js
const server = require('./server') // import server module
//const app = server.app
// start the API server and Web server
server.app.listen(server.port, () => {
console.log(`
\nExpress Server started on port ${server.port}..
APIs can be accessed at http://${server.host}:${server.port}${server.routePrefix}
Web Server started on port http://${server.host}:${server.port}
`)
})
api.js
'use strict'
const server = require('./server') // import sever module
const app = server.app
// get test route
app.get(server.routes.test, (req, res) => {
printDetails(server.routes.test, req.method, req.params, req.query)
res.send(server.routes.test)
})
module.exports = {
}
The problem I am facing is how do I use module.exports and what do I need to export from what module. My requirement is when I run "node serve.js", I should be able to run the APIs and they should be available for the client. How do I achieve this?
I think that the following structure is much more maintainable and easier to understand (also this is the more commonly used structure in new web apps in node):
├───client <-- Your web client application directory
│ └───assets
├───common <-- Common (shared) files between client and server
└───server <-- Your SugoiJS server directory
├───config <-- Build config (environment, webpack)
│ └───webpack
└───src <-- Your server app source code
├───app <-- Bootstrap module, Server initialize and listener files, 'authorization' class(optional)
│ └───classes
├───config <-- Server configuration (services, paths, etc.)
└───modules <-- All of you application modules
└───index <-- Single module
├───controllers <-- Modules' controllers
├───models <-- Modules' models(optional)
└───services <-- Modules' services
This is taken from SugoiJS framework.
https://wiki.sugoijs.com/get-started
Then basically all the routes you are writing will be controllers :)
There are many ways to split up JS code into files, the simplest way (that is still quite efficient) is to require modules. Here's a full guide, and here's a minimal demo:
// lib.js
module.exports = {
x: 10,
y: () => 20
}
// index.js
const lib = require('./lib');
console.log(lib.x); // 10
console.log(lib.y()); // 20
To keep the code clear, simple and efficient, requiring a module should not have any side effects (ie deleting a file, or making requests): for example, define a Class inside a module, and export it.
There are other ways to split up code between files, such as import/export.