Running a node express server using webpack-dev-server - node.js

I'm using webpack to run my react frontend successfully using the following config:
{
name: 'client',
entry: './scripts/main.js',
output: {
path: __dirname,
filename: 'bundle.js'
},
module: {
loaders: [
{
test: /.jsx?$/,
loader: 'babel-loader',
exclude: /node_modules/,
query:{
presets: ['es2015', 'react', 'stage-2']
}
}
]
}
}
I'm trying to put up a node.js express backend as well, and would like to run that through webpack as well, so that I have a single server running both the backend and frontend, and because I want to use babel to transpile my javascript.
I made a quick testserver looking like this:
var express = require('express');
console.log('test');
var app = express();
app.get('/', function(req, res){
res.send("Hello world from Express!!");
});
app.listen(3000, function(){
console.log('Example app listening on port 3000');
});
If I run this with node index.js and open my browser on localhost:3000 it prints "Hello world from Express!!". So far so good. Then I tried creating a web-pack config for it:
var fs = require('fs');
var nodeModules = {};
fs.readdirSync('node_modules')
.filter(function(x) {
return ['.bin'].indexOf(x) === -1;
})
.forEach(function(mod) {
nodeModules[mod] = 'commonjs ' + mod;
});
module.exports = [
{
name: 'server',
target: 'node',
entry: './index.js',
output: {
path: __dirname,
filename: 'bundle.js'
},
externals: nodeModules,
module: {
loaders: [
{
test: /\.js$/,
loaders: [
'babel-loader'
]
},
{
test: /\.json$/,
loader: 'json-loader'
}
]
}
}
When I run the command webpack-dev-server it starts up successfully (it seems). However, if I go to my browser on localhost:3000 now, it just says that the webpage is not available, just as when the server is not running at all.
I'm very new to both node and webpack, so either I have made a small mistake somewhere, or I'm way off ;)

Webpack-dev-server is great for client side development but it will not deploy Express api's or middleware. So in development I recommend running two separate servers: One for the client and one for your server side api's.
Nodemon npm install --save-dev nodemon is a good backend development server that will give you hot-redeploy of your api's, or you can just use express and restart when you make changes. In production the client and api will still be served by the same express server.
Set a lifecycle event for both nodemon and webpack-dev-server in your package.json to make starting them easy (example: npm run dev-server).
"scripts": {
"start": "webpack --progress --colors",
"dev-server": "nodemon ./server.js localhost 8080",
"dev-client": "webpack-dev-server --port 3000",
}
Or, to run express directly from node:
"scripts": {
"start": "webpack --progress --colors",
"dev-server": "node dev-server.js",
"dev-client": "webpack-dev-server --port 3000",
}
// dev-server.js
const express = require('express');
const app = express();
// Import routes
require('./_routes')(app); // <-- or whatever you do to include your API endpoints and middleware
app.set('port', 8080);
app.listen(app.get('port'), function() {
console.log('Node App Started');
});
Note: The api server must use a different port than webpack-dev-server.
And finally in your webpack-dev-config you need to use a proxy to redirect calls to your api to the new port:
devServer: {
historyApiFallback: true,
hot: true,
inline: true,
host: 'localhost', // Defaults to `localhost`
port: 3000, // Defaults to 8080
proxy: {
'^/api/*': {
target: 'http://localhost:8080/api/',
secure: false
}
}
},
// and separately, in your plugins section
plugins: [
new webpack.HotModuleReplacementPlugin({
multiStep: true
})
]
**Bonus points for having a single script to start and kill both

Since webpack-dev-server is just a tiny express server with compile on change and hot reload.
So, if you already got a express server for backend API, just merge the compile on change and hot reload into your express server.
Then after take a look at the package.json of webpack-dev-server, i find the key is just
webpack-dev-middleware
const express = require('express'); //your original BE server
const app = express();
const webpack = require('webpack');
const middleware = require('webpack-dev-middleware'); //webpack hot reloading middleware
const compiler = webpack({ .. webpack options .. }); //move your `devServer` config from `webpack.config.js`
app.use(middleware(compiler, {
// webpack-dev-middleware options
}));
app.listen(3000, () => console.log('Example app listening on port 3000!'))
So, when you run your BE server, it will compile all the things using webpack, and watch for changes, LOL ~
Also, add webpack-hot-middleware for hot reloading function, see Hot Module Replacement

From your questions here and here, it appears that you are using ReactJS with ES6. I faced the exact same issue, and here is how I tackled it -
Have multiple entry points for your application
In particular you can put all your vendor files like JQuery, React etc into one chunk. This way, your vendor files will remain the same even when you modify your souce files. You can add this line to your webpack config
entry: {
vendors: ['react','reactDom','jquery'] //Any other libraries
}
Use the CommonsChunkPlugin to have webpack determine what code/modules you use the most, and put it in a separate bundle to be used anywhere in your application.
plugins: [
new webpack.optimize.CommonsChunkPlugin('vendors', 'dist/js/vendors.js', Infinity),
]
Use React Hot Loader
Run npm install react-hot-loader --save-dev. Make sure you have installed webpack-dev-server first.
Then you need to change your loaders to this -
loaders: [
{
test: /\.jsx?$/,
loaders: ['react-hot'],
include: path.join(__dirname, 'public')
},{
loader: 'babel',
query: {
presets: ['react', 'es2015']
},
include: path.join(__dirname, 'public')
},
]
Make sure React Hot Loader comes before Babel in the loaders array. Also make sure you have include: path.join(__dirname, 'public') to avoid processing node_modules, or you may get an error like this -
Uncaught TypeError: Cannot read property 'NODE_ENV' of undefined
Modifications to your script tags in your index.html page
If your html has something like this -
<script src="/dist/js/vendors.js"></script>
<script src="/dist/js/app.bundle.js"></script>
Change this to point to your webpack-dev-server proxy -
<script src="http://localhost:8080/dist/js/vendors.js"></script>
<script src="http://localhost:8080/dist/js/app.bundle.js"></script>
Run webpack-dev-server --hot --inline,
wait for the bundling to finish, then hit http://localhost:3000 (your express server port) in your browser.
If you run into any errors, you could find this troubleshooting guide very useful.
Hope this helps, and you can take a look at the webpack setup for my project here

Just faced the same issue and came with another solution (found out more information about it later, but here it is).
Instead of using the webpack-dev-server, use the webpack --watch command so files are compiled again upon changes. Once the files are updated on the dist (or any other compiled files folder) you can set to run the nodemon on the dist folder and watch only the dist files.
This way it is possible to have the express server running and serving the front-end as you would in a production environment (or kinda) and benefit from the fast reloads.
Here's a link with some solutions to combine the webpack watch and nodemon.
My scripts section is something like this at this moment (I'm using the run-all solution):
"scripts": {
"serve": "npm-run-all --parallel serve:webpack serve:nodemon",
"serve:webpack": "webpack --progress --colors --watch",
"serve:nodemon": "nodemon ./dist/app.js --watch dist"
},

I found this to be a really simple solution that works with create-react-app, where you just want to use npm start to start the webpack-dev-server and you can't mess around with the webpack config. Just use http-proxy-middleware in Express to proxy all requests that the server doesn't itself handle to the webpack-dev-server:
import express from "express"
import { createProxyMiddleware } from "http-proxy-middleware"
const app = express()
// Put your web APIs here, for example:
app.get("/hello", (req, res) => {
res.send("Hello World")
})
...
// This goes after all your other routes:
if (!isProduction) {
app.use("*", createProxyMiddleware({ target: "http://127.0.0.1:3000", ws: true }))
}
app.listen(5000)
Note 1: To keep this simple, I am not using HTTPS. (Use environment variable HTTPS=false to have webpack-dev-server use HTTP.)
Note 2: You only want to create the proxy in development mode - in production, you would probably use express.static to serve your compiled single-page app.
Run npm start on your React project and start your Express server. Then (using the port numbers in the example code) browse to http://localhost:5000. You will see your React front-end and it will be able to send API requests to your Express server, all on port 5000. Hot module replacement works too!

quick answer: webpack-dev-server has an express built in, just use onAfterSetupMiddleware or onBeforeSetupMiddleware to get the app instance
module.exports = {
//...
devServer: {
onBeforeSetupMiddleware: function (devServer) {
if (!devServer) {
throw new Error('webpack-dev-server is not defined');
}
// **devServer.app is an express**
devServer.app.get('/some/path', function (req, res) {
res.json({ custom: 'response' });
});
},
},
};
tl;dr
there are some ways to make it work, the one above is what I love most, let's have a look at other work arounds
1.proxy: config webpack dev server with proxy
this way you need an extra process for backend only, that means an extra step to start and stop your service, still it's a good enough solution, simple and works
2.webpack-dev-middleware: a middleware for express
lack both documentation and maintainess, I was using it and made it work, but when some package updates it fails to work

Related

How to restart the SSR script in Vite?

I am using the Dev server provided by Vite. It provides a method to run the SSR code using the ssrLoadModule method on the dev server instance. This how I use it:
const vite = await createServer({
mode: 'development',
appType: 'custom',
root: path.join(process.cwd(), 'server'),
build: {
ssr: 'server/main.ts'
},
server: {
hmr: true
},
ssr: {
format: 'cjs',
target: 'node'
},
plugins: []
});
const loadedModule = await vite.ssrLoadModule('server/main.ts');
Here the above file runs the server/main.ts file and also imports another file user.ts. The main.ts file creates an Express app/server that listens on port 3000 which exposes CRUD APIs for User entity.
My question is how do I restart after the SSR script (main.ts) or any of its import dependencies get modified? I don't care if it is a full restart or HMR. Note, the HMR works for frontend code but not for backend SSR code.

Webpack when using readdirSync for require a library

I have a legacy code for my express app that read all routes files in specific dir and require them in a loop. Notice this code cant be changed:
app.js
const normalizedRoutes = fs.readdirSync(__dirname + '/src/routes/')
.map(routeFile => `/src/routes/${routeFile}`);
normalizedRoutes.forEach((normalizedRouteDir: string) => {
require(normalizedRouteDir)(app);
})
Now, I want to combine a Server Side Rendered application with the code above, using some JSX in routes files.
My problem is because the routes files are loaded on run time webpack not recognize them when creating the bundle.js file.
Therefore there are not routes files in the /src/routes/${routeFile} and when I run the bundle.js file I get an error message of:
Error: ENOENT: no such file or directory, scandir '/Users/******/build/src/routes/'
(the stars are for hiding full path)
webpack configs:
webpack.base.js
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
module.exports = {
plugins: [new MiniCssExtractPlugin()],
module: { //remain
rules: [
{
test: /\.(ts|js)x?$/,
loader:'babel-loader',
exclude: /node_modules/,
options:{
presets:[
'#babel/react',
['#babel/env',{targets:{browsers:['last 2 versions']}}]
]
}
},
{
test: /\.css$/i,
use: [MiniCssExtractPlugin.loader, "css-loader"],
},
],
}
};
webpack.server.js
const path = require('path')
const {merge} = require('webpack-merge')
const baseConfig = require('./webpack.base.js');
const webpackNodeexternals = require('webpack-node-externals');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const config = {
mode: "development",
entry: {
main:"./app.ts",
},
resolve: {
extensions: [".js", ".jsx", ".json", ".ts"],
},
node: {
__dirname: true
},
output: {
libraryTarget: "commonjs",
path: path.join(__dirname, "build"),
filename: "bundle.js",
},
target: "node",
//Avoid put node modules of server when sending to browser
externals: [webpackNodeexternals()]
}
module.exports = merge(baseConfig,config)
scripts from package.json:
"dev:server": "nodemon --watch build --exec \"node build/bundle.js\" ",
"dev:build-server": "webpack --config webpack.server.js --watch",
When I copy the route files (js files) to the build directory it works of course but that means I don't run webpack on these files and therefore I can't include JSX\es6 features inside these files.
So my question is:
Is there any possible way to make these requires identify by webpack/babel to add them to bundle.js and avoid the need for seperate files (bundle.js and routes files)
If we cant do it, how can I run webpack on a folder seperatly from the bundle.js output and create a route folder in the correct path but after processed by babel?
Thanks!
Instead of using a Webpack you can try using a programmatic interface of babel, and transpile the files before requiring them.
Here is the link https://babeljs.io/docs/en/babel-core

Use newrelic in nuxt

I am trying to add newrelic agent to my nuxt application. I have installed the needed package and added my license key and set an application name in newrelic.js configuration file:
npm i newrelic
cp node_modules/newrelic/newrelic.js .
nano newrelic.js
My problem is that I also need to require this configuration file at the top of my server.js file and since this file is dynamically created and placed under the .nuxt folder I have no idea how to do this.
In a standard nodejs application I would simply add the require('newrelic'); to the top of my startup script or perhaps add a new script entry in package.json looking something like this:
"scripts": {
"dev": "node -r newrelic.js app.js"
}
I ended up using express to solve this:
npm i express
touch server/index.js
We will now load newrelic in the server/index.js file and after that create our nuxt instance:
require('newrelic');
const express = require('express');
const consola = require('consola');
const { Nuxt, Builder } = require('nuxt');
const app = express();
// Import and Set Nuxt.js options
const config = require('../nuxt.config.js');
config.dev = process.env.NODE_ENV !== 'production';
async function start () {
// Init Nuxt.js
const nuxt = new Nuxt(config);
const { host, port } = nuxt.options.server;
// Build only in dev mode
if (config.dev) {
const builder = new Builder(nuxt);
await builder.build();
} else {
await nuxt.ready();
}
// Give nuxt middleware to express
app.use(nuxt.render);
// Listen the server
app.listen(port, host);
consola.ready({
message: `Server listening on http://${host}:${port}`,
badge: true
});
}
start();
I also updated the script section in my package.json:
"scripts": {
"dev": "cross-env NODE_ENV=development nodemon server/index.js --watch server",
"build": "nuxt build",
"start": "cross-env NODE_ENV=production node server/index.js"
}
Hope this can help anyone who faces the same kind of problem.
For anyone struggling with this I found a much simpler solution by using Nuxt modules and hooks.
Create a new file modules/newRelic.js with the following content:
module.exports = function () {
this.nuxt.hook("listen", () => {
require("newrelic");
});
};
Import the module in nuxt.config.js
modules: [
"~/modules/newRelic.js"
]
Don't forget to install newrelic (npm i newrelic) and paste newrelic.js into the applications root folder.
In Node.js, you can require a module with the -r [module] syntax (see Node.js docs) before your actual script starts up.
For Nuxt, alter your npm run scripts like this (instead nuxt start):
node -r newrelic node_modules/nuxt/bin/nuxt.js start
This way, Node loads NewRelic first, then Nuxt, and ensures NewRelic is able to instrument all dependencies. If you let Nuxt bootup first, NewRelic is not aware of some dependencies, e.g. express.
This is recommended by NewRelic, see their docs.

Use Nuxt programmatically without builder

I am using nuxt programmatically inside express with nuxt.render middleware like below
const { Nuxt, Builder } = require('nuxt')
const app = require('express')()
const api = require('../api')
app.use('/api', api)
let config = require('../nuxt.config.js')
config.dev = !(process.env.NODE_ENV === 'production')
// Init Nuxt.js
const nuxt = new Nuxt(config)
app.use(nuxt.render)
async function start() {
// Build only in dev mode
if (config.dev) {
const builder = new Builder(nuxt)
await builder.build()
}
// Listen the server
app.listen(port, host)
console.log('Server listening on ' + host + ':' + port)
}
start()
When I am developing the server api routes and make some changes to the server side api files and restart the server, the whole nuxt project builds everytime which takes too much time. This is inconvenient as there were no changes in the nuxt files, only changes in the api route files.
So after building once, I comment out the following lines:
if (config.dev) {
// const builder = new Builder(nuxt)
// await builder.build()
}
I then restart the server which of course does not start the nuxt builder. But then I am now not able to access nuxt on browser. The server api routes work but the nuxt page routes just show "Nuxt loading…" screen.
How can I use nuxt app in development mode without building it everytime?
It might be a valid use case, sometimes one doesn't want to use two servers for a very small api/ui pair. What I would suggest is also to have a detached mode which works through nuxt/proxy and you could run it whenever you are doing dev work. In detached mode your nuxt works separately and api runs also separately and nuxt imitates above setup via `nuxt/proxy. It's very easy to setup via adding smth like this in nuxt config
modules: [
['#nuxtjs/proxy', { "/api/": { target: 'http://localhost:888/api'} }]
]
In prod you could run as before.
You can use build.parallel, build.cache, and build.hardSource. This will dramatically speed up your build times after the initial build.
I do not recommend this for production builds though. This is how I have it in my code:
nuxt.config.js:
const isDev = process.env.NODE_ENV === "development";
module.exports = {
// ...
build: {
parallel: isDev,
cache: isDev,
hardSource: isDev
},
// ...
};
And in the package.json I set the NODE_ENV to production for the build script:
"scripts": {
"build": "NODE_ENV=production nuxt build"
}
P.S.: You might also need to set build.publicPath for the dev builds.

webpack-dev-middleware and Express - can't get them to collaborate

I am trying to set up my first project using Webpack and Express but somehow I am doing something wrong.
This is what I did:
1. CREATED SAMPLE PROJECT
Created a sample project using express-generator. My folder structure is something like:
express-project
-app.js
-webpack.config.js
-public
-javascripts
-modules
-build
2. SET UP HANDLEBARS
Set up handlebars as view/template engine and created a couple of routes
3. WEBPACK CODE
Created the Webpack specific code/configuration as follows
webpack.config.js
var webpack = require('webpack');
var path = require('path');
var webpackHotMiddleware = 'webpack-hot-middleware/client?path=/__webpack_hmr&timeout=2000&overlay=false';
module.exports = {
resolve: {
alias: {
handlebars: path.resolve('public/vendor/handlebars-v4.0.5.js'),
bootstrap: path.resolve('public/vendor/bootstrap/js/bootstrap.js'),
pubsub: path.resolve('public/vendor/ba-tiny-pubsub.js')
}
},
context: path.resolve('public/javascripts'),
entry: {
cart: ['./modules/cart', webpackHotMiddleware],
index: ['./modules/products.js', webpackHotMiddleware],
vendor: ['bootstrap', 'pubsub', webpackHotMiddleware]
},
output: {
path: path.resolve('public/javascripts/build'),
publicPath: 'javascripts/build/',
filename: '[name].js',
chunkFilename: "[id].js"
},
module: {
loaders: [
// some loaders here
]
},
plugins: [
new webpack.optimize.OccurenceOrderPlugin(),
new webpack.HotModuleReplacementPlugin(),
new webpack.NoErrorsPlugin()
]
}
app.js
// some code before
var app = express();
(function() {
// Step 1: Create & configure a webpack compiler
var webpack = require('webpack');
var webpackConfig = require(process.env.WEBPACK_CONFIG ? process.env.WEBPACK_CONFIG : './webpack.config');
var compiler = webpack(webpackConfig);
// Step 2: Attach the dev middleware to the compiler & the server
app.use(require("webpack-dev-middleware")(compiler, {
noInfo: false,
publicPath: webpackConfig.output.publicPath,
stats: {
colors: true
}
}));
// Step 3: Attach the hot middleware to the compiler & the server
app.use(require("webpack-hot-middleware")(compiler, {
log: console.log,
path: '/__webpack_hmr',
heartbeat: 10 * 1000
}));
})();
// some code after
4. JS CODE ON TEMPLATE
Then on the handlebars page I require the bundled javascripts
<script src="javascripts/build/common.js"></script>
<script src="javascripts/build/vendor.js"></script>
<script src="javascripts/build/cart.js"></script>
5. NPM START
Finally if I start the server using the standard npm start I see in the shell that webpack bundles everything with no errors but if I go to localhost:3000/ it does not find any of the assets created by Webpack. Instead if I run webpack to create the various bundles as if I were on production, everything is created correctly and it works as expected.
Hope someone can figure out what I am doing wrong.
Thanks
I managed to figure out what was causing the problem, by adding a slash in these 2 lines everything started to work properly:
context: path.resolve('public/javascripts/'),
path: path.resolve('public/javascripts/build/'),

Resources