react express pm2 deployment - reactjs

Scenario
I've made react app as well as a express server for API and both are separate. I've not included react folder in express app. I'm looking forward to deploy it using pre/post scripts using PM2 but I'm having hard time achieving exactly what is in my mind.
Goal
I want to run npm install for both client and server as I might remove/add package if needed later.
I'm thinking like after npm install I want to build react app and then move that folder for serving to express( I don't know if it's possible to give directory path which is out of parent for express static contents).
Then I want to start the express server which will eventually serve react build files.
For now my directory structure is
.
├── client
├── ecosystem.config.js
└── server
I'm mostly confused as I don't came across any resource where this is achieved. Also I'm not sure if this is even possible with pm2 deploy scripts or I need to write my own bash script which will do some stuff then pm2 will only start server.
This is only what I did which seems totally wrong
ecosystem.config.js
module.exports = {
apps : [{
name: 'API',
cwd: 'server',
script: 'server.js',
args: 'one two',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G',
env_production: {
NODE_ENV: 'production'
}
}],
deploy : {
production : {
user : 'node',
// host : '212.83.163.1',
// ref : 'origin/master',
// repo : 'git#github.com:repo.git',
// path : '/var/www/production',
'post-deploy' : 'cd client && npm run build'
}
}
};

Since you have both server and client together, I am assuming it is being developed in a monorepo.
For these kind of purposes I would suggest to go with yarn workspaces as it satisfies your first requirement on its own(npm install for both client and server).
To enable workspaces in yarn versions prior to 1.0, execute to enable it.
yarn config set workspaces-experimental true
Then add a package.json in the folder(workspace root) outside server and client folders along with whatever other package.json keys you require. Also install common dependencies directly here instead of installing them individually in both of the package.json files inside server and client. (Don't forget to delete node_modules folder inside them as a new one would be created in workspace root with all dependencies installed together during yarn install).
{
"private": true,
"workspaces": ["server", "client"]
}
Add the following to your server index.js file.
app.use(express.static(path.join(__dirname, 'public')));
app.get('*', (req,res) =>{
res.sendFile(path.join(__dirname+'/public/index.html'));
});
And add npm script to the package.json in workspace root.
{
...
"scripts": {
"start": "yarn --cwd client react-scripts build && mv ./client/build ./server/public && node ./server/index.js"
}
...
}

My idea for your problem :
You can combine package.json of both client and server.
I have some confuse with your question .
You can config as :
module.exports = {
apps : [{
name: 'API',
cwd: 'server',
script: 'npm run react && npm run express', //you must config npm run react && npm run express before use them
args: 'one two',
instances: 1,
autorestart: true,
watch: false,
max_memory_restart: '1G',
env_production: {
NODE_ENV: 'production'
}
}],
deploy : {
production : {
user : 'node',
// host : '212.83.163.1',
// ref : 'origin/master',
// repo : 'git#github.com:repo.git',
// path : '/var/www/production',
'post-deploy' : 'cd client && npm run build'
}
}
}
Tell me if it not solve your problem.

Related

How to create a production build of a create react app?

I am trying to create a production build for online deployment of create-react-app with an express js backend using docker.
This is my docker file:
FROM node:12.18.3
WORKDIR /app
COPY ["package.json", "package-lock.json", "./"]
RUN npm install --production
COPY . .
RUN npm run build
EXPOSE 80
CMD ["npm", "start"]
I am using npm run build but still the react developer tools tells that it is a development build and not a production build.
Also in the sources all the project files are visible which should not be visible:
I have tried adding the below statement in the .env file in the root directory of the create-react-app but the project files are still visible:
GENERATE_SOURCEMAP=false
It will be helpful if anyone can guide me how do I fix this and create a production build.
Thank you
The reason why your source files are visible is simple. This is due to CMD ["npm", "start"]. It basically uses the webpack-dev-server and your docker image is build in the development environment. In order to deploy your app to production, you need a web server like nginx, to serve your static files, then create a docker image and run in the container, of your choice.
You need a web server like nginx to serve the files from the build folder. npm run build just creates the build folder it doesn't have any impact on what happens when you run npm start.
For those looking for a solution, the issue can be fixed by creating a server.js file in the project directory with the following content:
const express = require('express');
const serveStatic = require('serve-static');
const path = require('path');
const app = express();
// create middleware to handle the serving the app
app.use("/", serveStatic ( path.join (__dirname, '/build') ) );
// Catch all routes and redirect to the index file
app.get('*', function (req, res) {
res.sendFile(__dirname + '/build/index.html')
});
// Create default port to serve the app on
const port = process.env.PORT || 3000
app.listen(port);
also in the docker file add:
CMD ["node", "server.js"]
instead of
CMD ["npm", "start"]
npm run build
creates a build directory with a production build of your app. Inside the build/static directory will be your JavaScript and CSS files. Each filename inside of build/static will contain a unique hash of the file contents. This hash in the file name enables long term caching techniques.

NextJS with Express : copying files to server and running "npm run start" fails

I am creating a NextJS application and I want to copy ONLY the production files to my server and deploy the application. This works perfectly fine when I do NOT use Express, however when i do use Express the app no longer runs on the server as it says it can not find the "pages" directory.
Project structure
|- .next
|- client
|- polyfills.js
|- components
|- CompA.js
|- CompB.js
|- etc
|- node-modules
|- ...
|- pages
|- index.js
|- page1.js
|- page2.js
|- etc
|- public
|- appIcon.png
|- scripts
|- utils.js
|- stylesheets
|- main.css
|- next.config.js
|- package-lock.json
|- package.json
My steps
I develop the application locally and test it my running "npm run dev". When the app is ready to deploy to a server I copy JUST the production files to the server and deploy the application. It is my understanding that the built application is ALL contained in the ".next" directory, therefore this is the only directory I copy to my server.
Here are my full steps in order to get the application on the server
Develop the application locally
Test locally using "npm run dev"
Test locally using "npm run build" and then "npm run start"
Application runs perfectly fine
Move the files to the server location and deploy
cp .next ~/myTestFolder
cp package.json ~/myTestFolder
cp package-lock.json ~/myTestFolder
cp next.config.js ~/myTestFolder
cd ~/myTestFolder
npm install
npm run build
npm run start
Application runs perfectly fine
The process fails when i use Express
As soon as I use Express, this process no longer works. I need to use express and dotenv so I can have a customizable basePath for my application which can differ on the different servers I deploy my application to (see my other stack overflow question : NextJS deploy to a specific URL path)
I did the following:
Install the new dependencies
npm install --save express dotenv
Create a ".env" file
BASE_PATH=/my-test-application/path-for-my-app
Update "next.config.js" to read the variables from ".env" and pass to WebPack so it can be used in ".js" files
// support getting variables from the server environment
const { parsed: localEnv } = require('dotenv').config()
const webpack = require('webpack')
module.exports = {
// Stop being being able to access pages without using proper URL
useFileSystemPublicRoutes: false,
webpack: function(cfg) {
// pass the environment variables to webpack so JS files can access them
cfg.plugins.push(new webpack.EnvironmentPlugin(localEnv))
....
}
....
}
Update the scripts in "package.json"
"scripts": {
"dev": "node ssr-server.js",
"build": "next build",
"start": "node ssr-server.js"
},
Create a "ssr-server.js" file to do the routing
const express = require('express')
const next = require('next')
const port = parseInt(process.env.PORT, 10) || 3000
const dev = process.env.NODE_ENV !== 'production'
const app = next({ dev })
const handle = app.getRequestHandler()
app.prepare().then(() => {
const server = express()
server.get('/my-test-application/path-for-my-app', (req, res) => {
return app.render(req, res, '/index', req.query)
})
server.get('/my-test-application/path-for-my-app/page1', (req, res) => {
return app.render(req, res, '/page1', req.query)
})
server.get('/posts/:id', (req, res) => {
return app.render(req, res, '/posts', { id: req.params.id })
})
server.all('*', (req, res) => {
return handle(req, res)
})
server.listen(port, err => {
if (err) throw err
console.log(`> Ready on http://localhost:${port}`)
})
})
Update any "Link"s to use this path
render(){
let basePath = process.env.BASE_PATH? process.env.BASE_PATH: "";
...
<Link href={`${basePath}/page1`}>
...
I can run the application locally using "npm run dev" and "npm run build"/"npm run start".
However when I copy files to my server location it now fails. I copy all the same files as above with the addition of the 2 new files
cp ssr-server.js ~/myTestFolder
cp .env ~/myTestFolder
I run "npm build" OK, but when I run "npm run start" it fails with the following
$ npm run start
> React_SSR#1.0.0 start /home/myTestFolder
> node ssr-server.js
/home/myTestFolder/node_modules/next/dist/lib/find-pages-dir.js:3
if(existsSync(_path.default.join(dir,'..','pages'))){throw new Error('> No `pages` directory found. Did you mean to run `next` in the parent (`../`) directory?');}throw new Error("> Couldn't find a `pages` directory. Please create one under the project root");}
^
Error: > Couldn't find a `pages` directory. Please create one under the project root
at findPagesDir (/home/myTestFolder/node_modules/next/dist/lib/find-pages-dir.js:3:170)
at new DevServer (/home/myTestFolder/node_modules/next/dist/server/next-dev-server.js:1:3830)
at createServer (/home/myTestFolder/node_modules/next/dist/server/next.js:2:105)
at Object.<anonymous> (/home/myTestFolder/ssr-server.js:5:13)
at Module._compile (internal/modules/cjs/loader.js:778:30)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:789:10)
at Module.load (internal/modules/cjs/loader.js:653:32)
at tryModuleLoad (internal/modules/cjs/loader.js:593:12)
at Function.Module._load (internal/modules/cjs/loader.js:585:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:831:12)
npm ERR! code ELIFECYCLE
npm ERR! errno 1
npm ERR! React_SSR#1.0.0 start: `node ssr-server.js`
npm ERR! Exit status 1
npm ERR!
npm ERR! Failed at the React_SSR#1.0.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm ERR! A complete log of this run can be found in:
npm ERR! /home/myTestFolder/.npm/_logs/2020-03-06T10_24_04_105Z-debug.log
Any idea what I am missing/doing wrong?
The error clearly says:
Error: > Couldn't find apagesdirectory..
you have to cp pages ~/myTestFolder as well.
Here is the solution.
On this way you don;t need fake copies to make server work properly.
Create and pass args to the next constructor :
// Add path module
const path = require('path')
// Init the Next app with args:
const app = next({ dir: path.join(__dirname, '..', 'yourwww'), dev })

How can I deploy my react project for production?

How can transform my react project into script to connect it to html page?
I am a new one in react please be tolerant. My boss demands to get completed script to connect it to html page without node and etc. What shall I do? Thank you.
Please check this url:
https://blog.bitsrc.io/react-production-deployment-part-3-heroku-316319744885
Also, Please check these steps:
In package.json, added this line to the scripts
"heroku-postbuild":
"NPM_CONFIG_PRODUCTION=false npm install --prefix client && npm run
build --prefix client".
Then added this
"engines": { "node" : "[your node version]" } after scripts.
In index.js, put the following code after your routes set up
if (process.env.NODE_ENV === "production") {
app.use(express.static("client/build"));
const path = require("path");
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build",
"index.html"));
});
}
I assume that you use git for version control and already install Heroku.
Open your terminal, then Heroku login -> Heroku create -> git push Heroku
master. If you do not get any error, you are a success to deploy your app.
Hope you will get it to work.
In order to get rid of node, you need to first build your project. If you've initialized your project with create-react-app, run this command:
npm run build
A folder named 'build' will appear in your project root containing your production app. Now the build folder is ready to build and you can serve it with a static server like 'serve'. To install 'serve' via npm, do this:
npm install -g serve
that's it! you can serve it now:
serve -s build
You can find out more about deployment here:
https://github.com/facebook/create-react-app/blob/master/packages/react-scripts/template/README.md#deployment
by using create-react-app
https://create-react-app.dev/docs/getting-started
https://create-react-app.dev/docs/deployment
dev: npm start or yarn start.
prod: npm run build or yarn build.

Need help getting pm2 to run a react app that has webpack configured in a dev server

Forgive me I am new to programming in general. I'm having trouble a lot of trouble trying to use pm2 to run a react app that has webpack set up. We are using an apache server for the production. For work we are trying to have the development server constantly running, the way that webpack dev server would have had it running so that more than one person can work on it at the same time. I thought I had enabled and got the app running but I am not sure if I am pointing to the wrong source file or if it should be pointing to a script or if I'm using the wrong interpreter.
I've looked into this https://github.com/facebook/create-react-app/issues/774 but having installed webpack the react scripts stopped working because of babel-cli. I can't afford to go back to react scripts at this point since I spent so much time trying to configure webpack.
Was wondering if anyone can guide me as to where the script in the pm2 should point to for react? is it the index.js? or is it the webpack.config file that i set up? And if I'm using webpack where should I point the interpreter to if i have webpack set up with babel?
Any help would be appreciated.
This is my pm2 config file.
module.exports = {
apps: [{
name: "ucdirectorapp",
script: "./src/index.js",
watch: [
"./build",
"./public",
"./src",
".babelrc",
"package.json"
],
watch_delay: 1000,
ignore_watch: ["node_modules"],
watch_options: {
"followSymlinks": false
},
env: {
name: 'ucdirector-dev',
NODE_ENV: 'development',
"PORT": 3000,
},
error_file: "./resource/log/pm2/err.log",
out_file: "./resource/log/pm2/out.log",
log_file: "./resource/log/pm2/log.log",
max_memory_restart: "500M",
interpreter: "./node_modules/#babel/cli/bin/babel.js"
}],
};
I've been using this script pm2 start config/server/pm2.config.js --watch --interpreter ./node_modules/#babel/cli/bin/babel.js in the package.json
I get this error
[PM2][ERROR] NVM is not available in PATH
[PM2][ERROR] Fallback to node in PATH
and I get a babel parse error. The app's status seem to be online but nothing is showing up on the host at port 3000 saying the site can't be reached and that it refused to connect.

Run distribution code using grunt

i am new in gruntjs. I have installed grunt. Also have app. I minified the code using grunt build. It created the /dist directory in which all minified versions of file is there.
How to run this distribution code(/dist) for production using grunt. Not able to figure it out. grunt serve command take /app directory by default
Use a package like grunt-contrib-connect (enter link description here). First install it:
$ npm install --save-dev grunt-contrib-connect
In your Gruntfile.js
grunt.loadNpmTasks('grunt-contrib-connect');
// ...
// In your config object
grunt.initConfig({
// ...
connect: {
dist: {
options: {
port: 8000,
base: 'dist',
keepAlive: true
}
}
}
// ...
});
Then run your grunt task:
$ grunt connect:dist
And navigate to http://localhost:8000 (or change port in config).
Note: if you use watch, set keepAlive: false.

Resources