How can I deploy next js app on an ubuntu vps - node.js

I'm working on project. where I have created the rest api with node / express.
I have deployed the rest api on my vps with nginx and pm2.
http://umer.pawnhost.com/
I have created the front-end with next js. I'm not using the api folder of next js.
I am using .env.local and next.config.js
I have looked at the next js documentation but they're saying either deploy on vercel or create a custom server.
I didn't get the custom server part
here's how my folder structure looks like:
client (Next JS)
------
.env.local
next.config.js
...
server (Node / Express)
------
...
package.json
.env
.gitignore
How can I deploy my next js app on the same vps?

You can add a frontend express server that handles routing for your nextjs app.
Create a server.js and add the following
const express = require('express');
const next = require('next');
const port = 3000;
const dev = process.env.APP_ENV === 'local';
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
server.get('*', (req, res) => {
return handle(req, res);
});
server.listen(port, err => {
if (err) throw err;
});
});
build and start the server
"build": "next build",
"start": "NODE_ENV=production node server.js",
This will start a server at the configured port

Related

React and Node app deployed on Azure web app shows JSON result instead of HTML page

I have a react app with node backend. I deployed it to Azure Web app , everything works fine.
When i open the url, i can see the application getting loaded and the data from the node app also getting fetched and displayed on the front end.
But when the user navigates to certain routes, it shows JSON which is served by the Node backend rather than a web page that should be displayed
Server.JS
import express from 'express';
import path from 'path'
import dotenv from "dotenv";
dotenv.config();
/* Create an Express application.*/
const app = express();
const __dirname = path.resolve();
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
routes.forEach(route => {
app[route.method](route.path, route.handler);
});
if (['production'].includes(process.env.NODE_ENV)) {
const root = path.join(__dirname,'..', 'client', 'build')
app.use(express.static(root));
console.log(root);
app.get("*", (req, res) => {
res.sendFile('index.html', { root });
})
}
const dBConnectionString = process.env.CON_STRING|| "";
const PORT = process.env.PORT || 8080;
db.connect(dBConnectionString)
.catch(err => {
console.error(err.stack);
process.exit(1)
})
.then(app.listen(PORT, () => {
console.log(`Server is running on port ${PORT}`);
}));
And the react app is deployed inside the wwwroot/site/client/build folder. I am using PM2 command as a startup command to run the app,
pm2 start ecosystem.config.js --no-daemon
and ecosystem.config.js
module.exports = {
apps: [{
name: "node",
script: "./server.js",
cwd: "/home/site/wwwroot/server/"
}
]
}
when i access this url https://web.azurewebsites.net/books/60ea9a3caffa68e153835489 it return a json where it should show a web page.
Should i run the the node app and react app in different ports? how to configure that in pm2?

Not able to prepare my MERN app for deployment

I have never deployed a MERN app to production before. This is going to be my first attempt and I am planning to deploy the app to digital ocean.
So, I have prepared my MERN app for deployment by following the instructions in a Udemy course. My app structure is as follows:
The following are the main changes that I have made to my application:
Because there will be no server created by create-react-app in production, I have written the production routing logic inside server/index.js that essentially says that for any routes not managed by app.use("/api/users", userRoutes) & app.use("/api/download", downloadRoutes), the backend server will server the index.html file inside the client/build folder, which I have created by running the command: npm run build.
server/index.js
const express = require("express");
const path = require("path");
const dotenv = require("dotenv");
const colors = require("colors");
const connectDB = require("./config/db");
const {
notFound,
globalErrorHandler,
} = require("./middleware/errorMiddleware");
const userRoutes = require("./routes/userRoutes");
const downloadRoutes = require("./routes/downloadRoutes");
dotenv.config();
connectDB();
const app = express();
app.use(express.json());
app.use("/api/users", userRoutes);
app.use("/api/download", downloadRoutes);
// Routing logic in production
if (process.env.NODE_ENV === "production") {
app.use(express.static(path.join(__dirname, "/client/build")));
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, "client", "build", "index.html"));
});
}
app.use(notFound);
app.use(globalErrorHandler);
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => {
console.log(`Server listening on port ${PORT}`.yellow.bold);
});
I have changed the process.env.NODE_ENV to production inside the .env file.
After the above-mentioned changes, when I run "npm start" (starts only the backend server) (not "npm run dev" that concurrently starts both the frontend and backend server) and visit http://localhost:5000, I should see my app. Instead, I see the following error.
What am I doing wrong?
As you can see in the error message, Express is looking for an index.html inside server/client/build which does not exist. Fix the path.
app.use(express.static(path.join(__dirname, '../client/build')))
You need to move your entire client folder inside the server and then add the following in the index.js file of the server:
if (process.env.NODE_ENV === "production") {
app.use(express.static("front/build"));
const path = require('path')
app.get("*", (req, res) => {
res.sendFile(path.resolve(__dirname, 'front', 'build',
'index.html'))
})
}
Make sure to run npm run build first.

Api returning 404 on production

I have a next.js app and I'm trying to create an api. When I run it as development, the api's get called, but when I run it using next start I get a 404 error when calling the api's.
Here's the relevant server.js code:
app.prepare().then(() => {
require('./server/startup/routes')(server);
server.get('*', (req, res) => {
return handle(req, res);
});
const PORT = process.env.PORT || 5000;
server.listen(PORT, err => {
if (err) throw err;
console.log(`> Read on http://localhost:${PORT}`);
});
});
Here's the routes file
module.exports = app => {
app.use('/api/something-cool', cool);
};
Cool File:
const express = require('express');
const router = express.Router();
router.post('/', async (req, res) => {
...Code
res.send({ status: 'ok' });
});
module.exports = router;
The api route of /something-cool works when I run nodemon, but when I run next run, it returns a 404 error. What am I doing wrong and how can I fix it?
You are using a custom server (express) on top of Next.js to customize routes. This means that first, you have to build the Next.js App and then you have to run your server.js file in order to serve the App.
Option 1:
Builds the production application first
next build
Then run you server.js file:
NODE_ENV=production node server.js
more info here https://github.com/zeit/next.js/tree/master/examples/custom-server-express
Option 2:
There is also the option to create the API route within the Next.js App without using a custom server.
See https://github.com/zeit/next.js/tree/master/examples/api-routes for more info on how to do it.

NextJS cookie-parser middleware absent from production build

I'm developing a NextJS application and have been using npm run dev during the development process. Now I'm trying to do a production build as described on the GitHub page.
My app blows up in production mode; it seems the cookie-parser node middleware is not installed in the production build? Here is how I set it up:
server.js
const express = require('express');
const next = require('next');
const cookieParser = require('cookie-parser'); // require cookie-parser
const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });
const handle = app.getRequestHandler();
app.prepare()
.then(() => {
const server = express();
server.use(cookieParser()); // use cookieParser
server.get('*', (req, res) => {
return handle(req, res);
});
server.listen(3000, (err) => {
if (err) throw err;
console.log('> Ready on http://localhost:3000');
});
})
.catch((ex) => {
console.error(ex.stack);
process.exit(1);
})
Later in the code I access the node req object. In development mode req.cookies is present as I would expect. In production mode it's absent.
It looks like there is no server.js file in the production build directory. What's more, grepping for cookie-parser and cookieParser in said production build directory yields empty results.
Any idea what's going on and how to get cookie-parser working in a production NextJS build?
Found the answer on the same GitHub page.
When using a custom server with a server file, for example called
server.js, make sure you update the scripts key in package.json to:
{
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "NODE_ENV=production node server.js"
}
}
One problem with the production build down!

Angular 2 cli with Express js

I want to use express js w/ node js to be my server for an angular 2 project. I was looking at tutorials for integrating express js w/ the angular cli (I followed this and this) and I had no luck. Does anyone have any suggestions on how to do this? Currently I have a new project up w/ cli just something that says "app works!" and want to try to do it using express as opposed to using the lite server. any help is appreciated!
this link works for me, but with some changes.
I install angular-cli and create new project, then I follow the tutorial of the link but with this changes:
I create a folder called node_server
In run npm install express --save in root folder of my project. This add server like dependencie in your package.json without create new on node_server.
I create a new file in node_server called server.js, with a basic express server. This server only returns the index.html of dist folder.
Added the scripts to the package.json, the scripts are the same in the link tutorial, but with one change, the filename is server.js not index.js
This is my server.js file :
const express = require('express');
var app = express();
var staticRoot = __dirname;
app.set('port', (process.env.PORT || 3000));
app.use(express.static(staticRoot));
app.get('/', function(req, res) {
res.sendFile('index.html');
});
app.listen(app.get('port'), function() {
console.log('app running on port', app.get('port'));
});
I hope this works for you.
The above comment worked pretty well for me,
just needed to make some adjustments to the scripts in package.json suggested in that link.
"build:nodeserver": "ng build && cp node-server/* dist",
"build:nodeserver-prod": "ng build -prod && cp node-server/* dist",
"serve-build" : "npm run build:nodeserver && nodemon dist/index.js",
"serve-build-prod": "npm run build:nodeserver-prod && nodemon dist/index.js"
I have a folder called node-server
which has my server.js file like above with routes for angular2 app as above,
and some api routes in there too, and some other folders in the node-server like mysql-requests and some files like config.json file
What I do create 2 folder on is your express server (I am using
Express-generator) and 1 is Angular 2 project (Angular cli )
root folder
--->server
--->ng4
---> gulpfile.js
gulp will build your app and move build files
var gulp = require('gulp')
var rename = require("gulp-rename");
var deletefile = require('gulp-delete-file');
var exec = require('child_process').exec;
var runSequence = require('run-sequence');
gulp.task('build', function (cb) {
exec('cd ng4/ && ng build', function (err, stdout, stderr) {
cb(err);
});
})
gulp.task('rename', () => {
return gulp.src("./ng4/dist/index.html")
.pipe(rename("ng4.html"))
.pipe(gulp.dest("./server/views"));
})
gulp.task('deletefile', function () {
var regexp = /\w*(\-\w{8}\.js){1}$|\w*(\-\w{8}\.html){1}$/;
gulp.src(['./server/public/index.html',
'./ng4/dist/**/*.*'])
.pipe(deletefile({reg: regexp,deleteMatch: false}));
});
gulp.task('move', function () {
return gulp.src(["./ng4/dist/**/*.*"])
.pipe(gulp.dest("./server/public"));
});
gulp.task('default', function(callback) {
runSequence('build','rename','move','deletefile',callback)
});
In this way you can developed in ng4 app and run gulp command on root .
I have this scenario; however, I want to be able to:
run my angular CLI project using ng serve instead of modifying any gulp tasks/ processes and without having to run ng build every time
have a server-side API responding on its own port independently of the frontend -- this way the microservice architecture pattern is used and concerns between frontend and backend are separated.
To accomplish this, I used concurrently with the below package.json script modifications
"scripts": {
"start": "concurrently \"npm run-script start-frontend\" \"npm run-script start-backend\"",
"start-frontend": "ng serve",
"start-backend": "node index.js",
...
My backend index.js file is boilerplate but looks like this...
const express = require('express');
const router = express.Router();
const app = express();
const PORT = process.env.PORT || 3000;
router.get('/', (req, res) => {
res.json({ Hello: 'World' });
});
app.use('/api', router);
app.listen(PORT, function() {
console.log(`API running on port ${PORT}`);
});
Now, when I want to run the app, I can type npm start to get everything running. Then, I can go to...
http://localhost:3000/api/ to access the backend
http://localhost:4200/ to access the frontend

Resources