Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 6 years ago.
Improve this question
How do you structure your project for full stack development, such that the js in /webapp could be processed by a gulp/grunt/webpack tools - in exploded war.
I know I can have maven/gradle add a step to perform some "npm thing", but that's a full build. I'll have that too, but I need the instant reload feature for development.
I want to be able to save a js file, have a watcher pick it up, process it and throw it back into my exploded war, in such a way that it a) doesn't pollute my /src/main/webapp/ b) I want it to coexist with my java webapp on the same root context (have my jsp's and eat it too).
One thing I considered is having my processes js outputed to some /src/main/webapp/do_not_checkin_folder and doing an equivalent of .gitignore on that directory. Followed up by, the full gradle/maven build that would rewrite paths in index.html removing any mention of do_not_checkin_folder and shortening the path to start from /src/main/webapp. Call it production mode.
Can someone suggest a different approach. What worked for you on your project?
I have a Node server running Express that proxies most requests to my Java web server, except for the js requests, which it uses webpack hot middleware to serve.
Webpack is configured to use the contents src/main/js with webpack-hot-middleware to generate the single js page.
So in development, you navigate the to Node Express server. In production, the static js file is served by the Java web server.
Here's my server.js for Node:
const PROXY_TARGET = 'http://localhost:9501';
const webpack = require('webpack');
const webpackDevMiddleware = require('webpack-dev-middleware');
const webpackHotMiddleware = require('webpack-hot-middleware');
const config = require('./webpack.config-hot-middleware');
const express = require('express');
const proxy = require( 'http-proxy' );
const proxyServer = proxy.createProxyServer( );
proxyServer.on('error', function(err, req, res) {
console.log('Proxy error: ' + err);
res.writeHead(500, {
'Content-Type': 'text/plain'
});
res.end('Proxy request to ' + PROXY_TARGET + ' failed. Please make sure your Java server is running!');
});
const app = new require('express')();
const port = 3000;
const compiler = webpack(config);
app.use(webpackDevMiddleware(compiler, { noInfo: true, publicPath: config.output.publicPath }));
app.use(webpackHotMiddleware(compiler));
app.use('/', function(req, res) {
proxyServer.web(req, res, {
target: PROXY_TARGET
});
});
app.listen(port, function(error) {
if (error) {
console.error(error);
} else {
console.info('==> Listening on port %s.);
}
});
Related
Background
I am migrating an Angular app in GKE cluster. The base docker image that I must use(company policy) does not have any options to install any new softwares like shell, Angular cli command ng etc. The base docker image has only Node installed.
There is a shared base url, let's say, www.my-company.com, that everyone has to use for app deployment with a path added after the base url like www.my-company.com/my-angular-app/ - all the other Angular apps must be differentiated using the path of the app.
What I did
Since I can't run ng serve command in the base image, I added Express dependency in the package.json in Angular application and created an express server to route the traffic to Angular app.
I was following this youtube video to configure the application - https://www.youtube.com/watch?v=sTbQphoYbK0&t=303s. The problem I am facing is to how I load the the static files in the application.
If I define absolute path inside sendFile method of server.js file, although the application is working, but in future, if I need to add any other files in the application, I have to create another route in server.js file.
I don't know how Express can search a file automatically from the static folder(and sub folders) and return only that file when needed. I defined a static folder too, but seems like it is not working.
Following is my server.js code
==============================
const express = require('express');
const http = require('http');
const path = require('path');
const port = 8080;
const contextPath = '/my-angular-app';
const router = express.Router();
const app = express();
app.use(contextPath, router);
app.listen(port, ()=> {
console.log("Listening on port: ", port);
});
app.use(express.static(__dirname + '/dist/testapp/'));
router.get('/', function(req, res) {
// to get index.html file
res.sendFile(path.resolve(__dirname + '/dist/testapp/index.html'));
});
router.get('/*', function(req, res) {
let path = __dirname +'/dist/testapp/' + req.path
console.log('full path: ', path);
// To return static files based on incoming request, I am facing problem here(I think)
res.sendFile(path);
});
==============================
I want Express will send any files based on file name in the request. It should also take care of nested directories in the /dist/testapp/ directory
/dist/testapp/ -> This is the directory where Angular generates code for my app after I execute ng build command
WEBAPP.get("/admin/script.js", (req, res) => {
console.log(req.path);
if (req.session.username !== "Admin") return res.render("error");
res.sendFile(__dirname + "/admin/admin.js")
});
WEBAPP.get("/admin", (req, res) => {
if (!req.session.loggedin) return res.render("error");
if (req.session.username !== "Admin") return res.render("error",);
res.render("admin", {
csrfToken: req.csrfToken(),
title: "ADMIN PORTAL",
username: req.session.username,
nav_avatar: GetImageURL(req.session.avatar, "small")
});
});
There's no need to publically share /admin/script.js in my case but if a user requests this URL say example.com/admin/script.js a check for username equaling "Admin" if all is okay we sendFile.
I would maybe assume that you're not properly targeting your static files. Perhaps console.log the target.
I have builded an app using Nuxt and I have created simply server middleware for handling email sending. Everything is working on dev but on production I have 404 error from that endpoint. Does anybody know how to include server-middleware into build files or any other way?
server-middleware:
const bodyParser = require('body-parser')
const app = require('express')()
app.use(bodyParser.json())
app.post('/', (req, res) => {
// Some code here
})
module.exports = app
Nuxt.config.js
serverMiddleware: [
{ path: '/contact/send', handler: '~/server-middleware/email.js' }
],
Response here:
I found an answer on github issue nuxt repo:
This is correct - server middleware aren't compiled or part of your
webpack build and so you should make sure to copy them separately into
production. (As well as making sure that any dependencies they have
are installed in production.)
(Note that this will change with Nuxt 3 - you'll be able to have a
single built server that includes your server middleware.)
Issue here:
https://github.com/nuxt/nuxt.js/issues/9158#issuecomment-820676790
Here is an in-depth answer regarding the whole setup of a serverMiddleware.
This solutions seems to work: https://github.com/nuxt/nuxt.js/issues/1486#issuecomment-325181524
// nuxt.config.js
serverMiddleware: [
'~/api/index.js',
]
// api/index.js
const app = require('express')()
module.exports = { path: '/api', handler: app }
app.get('/say/:word', (req, res) => {
res.json(req.params)
})
I'm trying to figure out how to best optimize my build file and ran across the notion of compressing text files like js and css. From what I've come across every article either assumes you have access to the webpack config file or you've ejected from CRA. I don't want to.
So I added a post build script to my package.json file:
"scripts": {
"build": "npm run build --prefix client",
"prod-compress": "gzip client/build/static/js/*.js && gzip client/build/static/css/*.css",
which results in the /client/build/static/js and /client/build/static/css folders looking like this:
I then went into my app.js file and added the following code:
app.get('*.js', function(req, res, next) {
req.url = req.url + '.gz';
res.set('Content-Encoding', 'gzip');
res.set('Content-Type', 'text/javascript');
next();
});
app.get('*.css', function(req, res, next) {
req.url = req.url + '.gz';
res.set('Content-Encoding', 'gzip');
res.set('Content-Type', 'text/css');
next();
});
If I understand what's happening correctly, the f/e /client/public/index.html file will still reference a regular .js file. However, when the file is requested from the server, it will respond with the .js.gz file.
However, when I compress the files the entire site goes blank, like it can't find anything to serve up.
If you don't mind adding a new dependency, I would recommend using express-static-gzip which will automatically serve your compressed files:
const express = require("express");
const expressStaticGzip = require("express-static-gzip");
const app = express();
const buildPath = path.join(__dirname, '..', 'build', 'static');
app.use("/", expressStaticGzip(buildPath);
You also have the choice to add other types of compression like brotli by passing an options object:
const buildPath = path.join(__dirname, '..', 'build', 'static');
app.use(
'/',
expressStaticGzip(buildPath, {
enableBrotli: true,
orderPreference: ['br', 'gz']
})
);
Brotli gives you even more optimized files than gzip but it's not supported by all browsers. Thankfully, express-static-gzip automatically picks the correct file to send based on the Accept-Encoding/Content-Encoding header the user's browser sends to it.
If you want to use brotli, I recommend taking a look at compress-create-react-app. It's specifically made for React apps but should work with any files.
Disclaimer: I'm the author of compress-create-react-app
I'm trying to add Log4js-Node to a Node.js server running on Apache. Here's my code:
const path = require("path");
const express = require("express");
const log4js = require('log4js');
const app = express();
const logger = log4js.getLogger();
logger.level = "debug";
const port = 443;
log4js.configure({
appenders: { everything: { type: 'file', filename: 'logs.log', flags: 'w' } },
categories: { default: { appenders: ['everything'], level: 'ALL' } }
});
const server = app.listen(port, () => {
logger.debug("listening to requests on port " + port);
});
app.get("/log", (req, res) => {
res.sendFile(path.join(__dirname + "/logs.log"));
});
When I run the script on Node.js on my computer and navigate to localhost:443/log I see what I expect, which is this:
[2020-03-17T22:50:43.145] [DEBUG] default - listening to requests on port 443
But when I run the code on a remote server it crashes and I get this in the error page (with part of the path replaced by me with "[removed]"):
App 25925 output: at Server. ([removed]/index.js:27:9)
App 25925 output: at Logger. [as debug] ([removed]/12/lib/node_modules/log4js/lib/logger.js:124:10)
App 25925 output: at Logger.log ([removed]/12/lib/node_modules/log4js/lib/logger.js:73:12)
App 25925 output: at Logger._log ([removed]/12/lib/node_modules/log4js/lib/logger.js:90:16)
App 25925 output: at Object.send ([removed]/12/lib/node_modules/log4js/lib/clustering.js:97:15)
App 25925 output: [removed]/12/lib/node_modules/log4js/lib/clustering.js:97
App 25925 output: at Object. ([removed]/12/lib/node_modules/log4js/lib/clustering.js:8:13)
I'm using A2 Hosting which uses Apache 2.4.41. I opted for Node.js 12.9.0, and Log4js 6.1.2. The package.json should be the same on both my computer and the server, and I've run npm install on both.
Is this just an issue with Log4js and the server, or have I missed something somewhere?
This was actually a relatively simple fix. The path referenced by the last error in the stack trace is a Log4js module that implements clustering support through Node's "cluster" module. The line "8" referenced is cluster = require("cluster"). It's wrapped in a try/catch block like this:
try {
cluster = require("cluster"); //eslint-disable-line
} catch (e) {
debug("cluster module not present");
disabled = true;
}
The installation of Node.js on my computer came with the "cluster" module, however as far as I can tell, the server I'm using doesn't support it. Also, the version of Node I'm using on my computer is newer than what I'm using on the server (so I've now installed 12.9 on my machine). I believe the older version of Node doesn't bother trying to catch the exception and tries to load the cluster module, fails, and then throws the error.
So the simple fix was to comment out most of the "try/catch" block, leaving just the contents of "catch" like this:
// try {
// cluster = require("cluster"); //eslint-disable-line
// } catch (e) {
debug("cluster module not present");
disabled = true;
// }
If someone has a better fix, I'm open to suggestions.
The same response of #skittleswrapper,thx, it work for me.
I use Node.js 14.18.1 with log4js 6.3.0.
But i wondering what'is the necessary of this module 'cluster' and if we can
add it to our app in other way.
I'm new to JavaScript/Nodejs. How can I share my configuration across the Nodejs application. For example: I have a config/config.coffee
path = require("path")
module.exports = {
development:
db: 'mongodb://localhost/hello'
root: rootPath = path.normalize(__dirname + '/..')
}
I included config.coffee in my app.coffee.
express = require("express")
# Load configurations
env = process.env.NODE_ENV || 'development'
config = require("./config/config")[env]
require('./config/boot')
app = express()
Now I want to include config variable into my config/boot.coffee. How can I do it? I don't want to re-include config/config.coffee into config/boot.coffee. Here is the my config/boot.coffee file:
env = process.env.NODE_ENV || 'development'
config = require("./config")[env]
fs = require("fs")
mongo = require("mongoose")
# Bootstrap db connections
mongo.connect config.db
# Bootstrap models
models_path = config.root+"/app/models"
fs.readdirSync(models_path).forEach( (file)->
require(models_path + '/' + file) if ~file.indexOf('.coffee')
)
# Bootstrap services
services_path = config.root+"/app/services"
fs.readdirSync(services_path).forEach( (file)->
require(models_path + '/' + file) if ~file.indexOf('_service.coffee')
)
Sorry for bad English :(
You might want to check out nconf, which helps you keep a kind of "waterfall" approach to application configuration, which allows you to mix your configuration from different sources very transparently.
You can see nconf in action in this project I wrote, unbox, which is basically boilerplate I use for applications I write on Node. You can check out how configuration is loaded here.
You could use something like grunt-pemcrypt for increased security by checking in the secure, encrypted file, and saving the encryption key somewhere safe.
12factor also has a nice approach to application configuration you might want to look into.
I believe NodeJS caches your require's, so calling require('config') again won't cause any performance degradation.
http://nodejs.org/api/globals.html#globals_require