Deploying Nuxt.js SSR app to production gives error - javascript

I have built a Universal app with Nuxt.js, and now I want to deploy it, however I am finding the process very confusing.
This is the scripts part of 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",
"generate": "nuxt generate"
}
So far I have been using npm run dev with no problem. When I run npm run build it all seems to work fine, then when I run npm run start the app also runs succesfully, but it doesn't seem to be executing my recent built code, instead, it rather seems to be running the same dev code that was ran when doing npm run dev.
I have also tried to execute nuxt start, which made sense to run after executing npm run build, however the app crashes if I do so.
How do I run my SSR app in production after building it?
Is npm run build supposed to build any of my Node.js backend stuff?
This is my server/index.js
// Debug in development
process.env.DEBUG = 'nuxt:*'
require('dotenv').config()
const env = require('../config')
const express = require('express')
const consola = require('consola')
const { Nuxt, Builder } = require('nuxt')
const app = express()
const redis = require('./api/adapters/Redis')
const host = env.public.host
const port = env.public.port
app.use(function(req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
next();
});
app.set('port', port)
// Import and Set Nuxt.js options
let config = require('../nuxt.config.js')
config.dev = !(env.public.environment === 'production')
async function start() {
// Init Nuxt.js
const nuxt = new Nuxt(config)
// Build only in dev mode
if (config.dev) {
const builder = new Builder(nuxt)
await builder.build()
}
// Connect to redis
redis.connect() // <-- This creates a connection with redis server, and doesn't seem to connect when running `nuxt start`, hence the error.
require('fs').writeFile('ZZZ.txt', 'YES', 'utf8', () => {}) // <-- as an experiment ZZZ.txt file gets written when `npm run dev` but not when `nuxt start` is ran.
// Handle errors
process.once('uncaughtException', err => {
redis.disconnect()
process.exit(1)
});
// 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()

Related

I can’t serve subroutes when I reload the page

It’s the first time I deployed a full-stack app I created on Render and all seems to be working fine except for the fact that my routes are not found when I reload the component. I’ve got a general idea why as I did a research online but I haven’t managed to solve the problem yet.
Here’s my server file
const express = require("express");
const dotenv = require("dotenv");
const connectDB = require("./config/db");
const path = require("path");
const cors = require("cors");
const corsOptions = {
origin: "*",
credentials: true,
optionSuccessStatus: 200,
};
const app = express();
app.use(cors(corsOptions));
// read variables and save them as environment variables
dotenv.config({path: "./.env"});
// Init Middleware
app.use(express.json({extended: false}));
// data from req object is added to it(middleware)
app.use(express.json());
// Define Routes
app.use("/api/data", require("./routes/data"));
app.use("/api", require("./routes/collection"));
app.use(`/api/item`, require("./routes/item"));
app.use(`/api`, require("./routes/under"));
app.use("/api/users", require("./routes/users"));
app.use("/api/auth", require("./routes/auth"));
app.use("/api/email", require("./routes/email"));
app.use("/api/basket", require("./routes/basket"));
app.use("/api/size", require("./routes/size"));
app.use("/api/wishlist", require("./routes/wishlist"));
app.use("/api/checkout", require("./routes/checkout"));
app.use("/api/payment_confirmation", require("./routes/confirmation"));
const PORT = process.env.PORT || 5000;
// Connect to Database
connectDB();
// Load React App in production
if (process.env.MODE === "production") {
app.use(express.static(path.join(__dirname, "build")));
app.get("*", (req, res) => res.sendFile(path.resolve(__dirname, "build", "index.html")));
} else {
app.get("/", (req, res) => {
res.send("Welcome to the home page");
});
}
app.listen(PORT, () => {
console.log(`App running on port ${PORT}`);
});
That's my package.json and my scripts
“scripts”: {
“start”: “node app.js”,
“server”: “nodemon app.js”,
“client”: “npm start --prefix …/client”,
“client:install”: “npm install --prefix client”,
“build”: “npm install --prefix client && npm run build --prefix client”,
"dev": "concurrently \"npm run server\" \"npm run client\""
}
That's my repo if you want to have a look at the file structure.
enter link description here
enter link description here
I get this error on the console
Error: ENOENT: no such file or directory, stat '/opt/render/project/client/build/index.html'
Although on the events log it says that build is successful so I'm not sure what the correct folder would be.
Thanks in advance
When I downloaded the app and ran it on my system reloading the page, it seems like the server cannot find the routes defined in your server file. This is because your server is configured to handle all routes in your React app's index.html file.
your client-side routes will match the routes defined in your React app, and the server will only serve the index.html file for the initial request.
In your server file, modify your app.get("*")method to serve the index.html file only for the initial request:
if (process.env.MODE === "production") {
app.use(express.static(path.join(__dirname, "build")));
app.get("*", (req, res) => {
// Serve index.html for the initial request
if (req.originalUrl === '/') {
res.sendFile(path.resolve(__dirname, "build", "index.html"));
} else {
// Serve static files for all other requests
res.sendFile(path.join(__dirname, "build", req.originalUrl));
}
});
}

NodeJs Testrunner freezes / stops when setting up Express server

I'm using Node v18 with the experimental testrunner. I use express as a dev dependency for http integration tests which works fine but there is one test freezing or stopping the testrunner ( it doesn't continue )
I'm using TS but can also reproduce it with JS, the test file HttpTests.js contains
import assert from 'assert/strict';
import express from 'express';
import test from 'node:test';
test('Http', async () => {
const server = express();
server.listen(3000);
assert.ok(false);
});
Running this with the npm script "test": "node --test $(find . -name '*Tests.js')" breaks the test runner.
Any ideas what is wrong or missing?
Why am I not using the default execution model?
Since I'm using TS I had to find a way to use ts-node with the testrunner. You can find more information here
https://github.com/nodejs/node/issues/43675
So currently my TS project is using this npm script, which works fine
Reproduction
I created a minimal reproduction repository with and without TypeScript
https://github.com/matthiashermsen/reproduce-broken-test-ts
https://github.com/matthiashermsen/reproduce-broken-test-js
For reproduction purposes run mkdir reproduction && cd reproduction && npm init -y && npm install express. After that create a test directory with a file HttpTests.js containing the content as shown above. Change the package.json to
{
"name": "reproduction",
"type": "module",
"scripts": {
"test": "node --test $(find . -name '*Tests.js')"
}
}
and run the script, the testrunner should not finish.
The testrunner is still experimental
Yes I know. But there are many tests in the project that work perfectly fine. Some sample code
await t.test('subtest - saves data.', async () => {
const expectedResult = {};
const api = express();
const port = await getRandomPort();
const server = api
.use(express.json())
.post('/save', (request, response) => {
response.json(expectedResult);
})
.listen(port);
const httpDataProvider = new HttpDataProvider({ url: `http://localhost:${port}` });
const actualResult = await httpDataProvider.saveSomething();
assert.deepEqual(actualResult, expectedResult);
server.close();
});
The issue is the async activity that you start (server.listen()) but don't stop before the test errors out (by an exception thrown by assert.ok(false)).
Your second test case will probably also stall if actualResult doesn't deep-equal expectedResult because of the same issue (server.close() won't be called).
A workaround would be to always make sure the server gets closed in the end:
test('Http', async () => {
const app = express();
const server = app.listen(3000);
try {
assert.ok(false);
} finally {
server.close();
}
});
Most test frameworks provide "before/after" functionality that can be used to set up or tear down auxiliary objects before and after a test.

Next Export script is not working with next/image component

I have a project. I want to build and export it but there is an error.
Here is my build script in package.json file.
"scripts": {
"build": "next build && next export"
}
Here is my component where I'm using next/image
import Image from 'next/image'
export default function Logo(){
return(
<Image
src={process.env.website_url ? (process.env.website_url + "/img/logo/logo1.png") : "/img/logo/logo1.png" }
width={width}
height={height}
alt="logo"
/>
)
}
And here is the error when I want to build the project:
Error: Image Optimization using Next.js' default loader is not compatible with `next export`.
So, what sould I do now. Should I use a different loader or what? Thanks.
EDIT: I have created a server.js file and revised package.json scripts.
Server.js file:
const express = require('express')
const server = express()
const next = require('next')
const dev = process.env.NODE_ENV !== 'production'
const app = next({dev})
const handle = app.getRequestHandler()
const port = 3000
app.prepare().then(()=>{
server.get('*', (req, res)=>{
return handle(req,res)
})
server.listen(port, (err) => {
if(err) console.log(err)
console.log(`SERVER SUCCESS: ${port}`)
})
}).catch((e)=>{
console.log(e.stack)
process.exit(1)
})
Scripts in package.json file:
"scripts": {
"dev": "node server.js",
"build": "next build",
"start": "node server.js"
}
Server is working on the localhost, but not working on the host. There is a 503 Service Unavailable error.
next export is only for static pages. So next/image are images optimized and loaded with server started by next start (They are images on demand not on build). next start starts server which has Image optimization API. That's why its unable to export. Try using other image loading service. You can also try only building the next js (next build) without next export and use next start to start server which will handle the Image optimization.
You can Also configure your own loader for Image optimization. Refer these :
Loader reference
next-optimized-images
Next JS Doc message

What is the difference to run React server-side rendering locally and on real NodeJS server (IBM Bluemix)

I created a simple react app with serverside rendering using this workshop git as a base with my minor changes.
So when I run locally NODE_ENV=server node server.js it works fine. But my attempts to deploy this app on a trial of Bluemix the Nodejs server failed. Here's a log :
Here is my server.js code:
require('babel-register')
const express = require('express')
const React = require('react')
const ReactDOMServer = require('react-dom/server')
const ReactRouter = require('react-router')
const StaticRouter = ReactRouter.StaticRouter
const _ = require('lodash')
const fs = require('fs')
const PORT = 5050
const baseTemplate = fs.readFileSync('./index.html')
const template = _.template(baseTemplate)
const App = require('./js/App').default
const server = express()
server.use('/_public', express.static('./_public'))
server.use((req, res) => {
const context = {}
const body = ReactDOMServer.renderToString(
React.createElement(StaticRouter, {location: req.url,
context: context},
React.createElement(App))
)
res.write(template({body: body}))
res.end()
})
console.log('listening on port', PORT)
server.listen(PORT)
P.S. It's obvious that it doesn't understand ES6 syntax in js/App.js, but on my local server it works.
By default NODE_ENV=production but according to Bluemix docs I created a file in .profile.d directory
node_env.sh code:
export NODE_ENV=server;
But I'm not sure if this file changes node_env.
I'm hoping someone more knowledgeable than me can offer a better solution, but here is what I did to make your app work. There is probably a better answer.
Assuming that you do NOT want to run in production mode...
1) server.js: Listen to the port as set in the PORT env var.
server.listen(process.env.PORT || PORT)
2) package.json: Add start command in scripts
"start": "babel-node server.js --presets es2015,stage-2"
3) Get babel-cli
npm install --save-dev babel-cli
npm install --save-dev babel-preset-es2015 babel-preset-stage-2
4) Create a manifest.yml to set CF properties
applications:
- name: rvennam-node-react
memory: 1G
disk_quota: 2G
env:
NPM_CONFIG_PRODUCTION: false
NODE_ENV: dev
5) remove eslint dependencies from devDependencies in package.json (there was a mismatch)
Again, this is assuming you want to run on Bluemix under dev mode. If you wanted production on Bluemix, I would think you would want to use webpack to build locally, and then push and serve your dist directory.

Node.js - Auto Refresh In Dev

I am trying to improve the DEV experience in my Node. To do that, I want to:
a) restart my server when server-side code is changed
b) refresh the browser when client-side code is changes. In an effort to accomplish this, I began integrating nodemon and browserSync into my gulp script.
In my gulp script, I have the following task:
gulp.task('startDevEnv', function(done) {
// Begin watching for server-side file changes
nodemon(
{ script: input.server, ignore:[input.views] })
.on('start', function () {
browserSync.init({
proxy: "http://localhost:3002"
});
})
;
// Begin watching client-side file changes
gulp.watch([ input.css, input.js, input.html ], function() { browserSync.reload(); });
done();
});
When the above task runs, my browser opens to http://localhost:3000/. My app is visible as expected. However, in the console window, I notice:
Error: listen EADDRINUSE :::3002
I understand to some extend. I have app.set('port', process.env.PORT || 3002); in my server.js file. Yet, I thought that was purpose of setting the proxy value. Still, whenever I make a code change, I see the following related error in my console window:
[07:08:19] [nodemon] restarting due to changes...
[07:08:19] [nodemon] starting `node ./dist/server.js`
events.js:142
throw er; // Unhandled 'error' event
^
TypeError: args.cb is not a function
at Object.init (/Users/me/Website/Develop/node_modules/browser-sync/lib/public/init.js:25:25)
at null.<anonymous> (/Users/me/Website/Develop/gulpfile.js:142:25)
at emitNone (events.js:73:20)
at emit (events.js:167:7)
at Object.run (/Users/me/Website/Develop/node_modules/nodemon/lib/monitor/run.js:97:7)
at Function.run.kill (/Users/me/Website/Develop/node_modules/nodemon/lib/monitor/run.js:221:7)
at null.<anonymous> (/Users/me/Website/Develop/node_modules/nodemon/lib/monitor/run.js:333:7)
at emitOne (events.js:83:20)
at emit (events.js:170:7)
at restartBus (/Users/me/Website/Develop/node_modules/nodemon/lib/monitor/watch.js:162:7)
Me-MBP:Develop me$ events.js:142
throw er; // Unhandled 'error' event
^
Error: listen EADDRINUSE :::3002
at Object.exports._errnoException (util.js:856:11)
at exports._exceptionWithHostPort (util.js:879:20)
at Server._listen2 (net.js:1238:14)
at listen (net.js:1274:10)
at Server.listen (net.js:1370:5)
at Object.<anonymous> (/Users/me/Website/Develop/dist/server.js:70:8)
at Module._compile (module.js:399:26)
at Object.Module._extensions..js (module.js:406:10)
at Module.load (module.js:345:32)
at Function.Module._load (module.js:302:12)
At this point, my code changes do not appear in my browser. I do not understand what I'm doing wrong. I suspect I have my ports misconfigured. But, I'm not really sure how they should be setup.
By default BrowserSync uses port 3000. BrowserSync also uses port 3001 for the BrowserSync UI. For these two reasons, I thought I would set the port to 3002 in my server.js file and create the proxy shown above. What am I doing wrong?
You actually don't need to use gulp for this to work.
a) restart my server when server-side code is changed
Install nodemon globally using npm i -g nodemon then on your app folder do nodemon or nodemon ${index-file-of-your-app}.
b) refresh the browser when client-side code is changes.
Use browserify or webpack. I prefer using webpack; you may need to learn about the configuration a little bit but the good thing with webpack is that you don't need to refresh it. Once changes are found the changes will be reflected on the browser automatically. https://github.com/webpack/docs/wiki/hot-module-replacement-with-webpack
You can livereload both front and backend changes to the browser by using the 'livereload', 'connect-livereload', and 'nodemon' packages together. Also, this way you don't need Gulp or Grunt. Here's how the packages play together:
livereload opens a high port and notifies the browser of changed public files
connect-livereload monkey patches every served HTML page with a snippet that connects to this high port
nodemon is then used to restart the server on changed backend files
Set up livereload in Express
Set up the Express to both start livereload server watching the public directory and ping the browser during nodemon-induced restart:
const livereload = require("livereload");
const connectLivereload = require("connect-livereload");
// open livereload high port and start to watch public directory for changes
const liveReloadServer = livereload.createServer();
liveReloadServer.watch(path.join(__dirname, 'public'));
// ping browser on Express boot, once browser has reconnected and handshaken
liveReloadServer.server.once("connection", () => {
setTimeout(() => {
liveReloadServer.refresh("/");
}, 100);
});
const app = express();
// monkey patch every served HTML so they know of changes
app.use(connectLivereload());
Start Express with nodemon
Then you'd start the server with nodemon, for example, with a dedicated watch script by running npm run watch.
The key point here is to ignore the public directory that's already being watched by livereload. You can also configure files with non-default extensions, like pug and mustache, to be watched.
"scripts": {
"start": "node ./bin/www",
"watch": "nodemon --ext js,pug --ignore public"
},
You can read a longer explanation in "Refresh front and backend changes to browser with Express, LiveReload and Nodemon."
#mateeyow is right.
But if you want the browser to reload automaticaly, you also need livereload-plugin.
Enable webpack-hot-replacement only replace code in browser's memory, livereload-plugin do reload it.
See rock for example: https://github.com/orange727/rock/blob/master/app/templates/webpack/webpack.make.js#L255
Just as:
webpackConfig.plugins: [
new webpack.HotModuleReplacementPlugin(),
new LiveReloadPlugin({
appendScriptTag: true,
port: config.ports.livereload,
})];
I might be missing some context (e.g. I'm not sure what input represents), however, I think the npm module reload might solve your problem. Here's an example from the npm package page:
var express = require('express')
, http = require('http')
, path = require('path')
, reload = require('reload')
, bodyParser = require('body-parser')
, logger = require('morgan')
var app = express()
var publicDir = path.join(__dirname, '')
app.set('port', process.env.PORT || 3000)
app.use(logger('dev'))
app.use(bodyParser.json()) //parses json, multi-part (file), url-encoded
app.get('/', function(req, res) {
res.sendFile(path.join(publicDir, 'index.html'))
})
var server = http.createServer(app)
//reload code here
//optional reload delay and wait argument can be given to reload, refer to [API](https://github.com/jprichardson/reload#api) below
reload(server, app, [reloadDelay], [wait])
server.listen(app.get('port'), function(){
console.log("Web server listening on port " + app.get('port'));
});
The EADDRINUSE error is normally due to a connection already open on the specified port. This is probably due to a previous instance of the connection not being correctly closed when restarting the app.
Take a look at this gist and in particular try something like this in your gulp file:
'use strict';
var gulp = require('gulp');
var browserSync = require('browser-sync');
var nodemon = require('gulp-nodemon');
gulp.task('default', ['browser-sync'], function () {});
gulp.task('browser-sync', ['nodemon'], function() {
browserSync.init(null, {
proxy: "http://localhost:3002"
});
});
gulp.task('nodemon', function (cb) {
var started = false;
return nodemon({
script: 'app.js'
}).on('start', function () {
// to avoid nodemon being started multiple times
if (!started) {
cb();
started = true;
}
});
});

Categories