How to reload one file on Express without restarting? - node.js

I am appending a new route to a route JavaScript file with fs.appendFileSync(...), but, as Node.Js needs to be restarted to reload files, the appended route cannot be accessed. I only need to reload the file that is being appeded to.
var express = require('express');
var router = express.Router();
module.exports = router;
/* New Routes are appended here.
Example:
router.get('/bobby', function(req, res, next){
});
*/
I've already searched Stack Overflow on ways to reload files without restarting the server, but all of the questions only involve the automated reloading for development, not the real thing. I've seen the suggestions for Nodemon and Node-Supervisor already, but those say it is just for development.
This may be a dumb question, but I don't know how to solve it.
Thank you for reading.

You can use require-reload to do that. You should have a file where you import the router.js module, to which you are appending new code, that should look like:
var reload = require('require-reload')(require),
var router = reload('router.js');
Each time you append new code you should do:
try {
router = reload('router.js');
} catch (e) {
console.error("Failed to reload router.js! Error: ", e);
}
The express-route-refresh module may come in handy too. In fact, internally it also uses require-reload.

Related

Register new route at runtime in NodeJs/ExpressJs

I want to extend this open topic: Add Routes at Runtime (ExpressJs) which sadly didn't help me enough.
I'm working on an application that allows the creation of different API's that runs on NodeJs. The UI looks like this:
As you can see, this piece of code contains two endpoints (GET, POST) and as soon as I press "Save", it creates a .js file located in a path where the Nodejs application is looking for its endpoints (e.g: myProject\dynamicRoutes\rule_test.js).
The problem that I have is that being that the Nodejs server is running while I'm developing the code, I'm not able to invoke these new endpoints unless I restart the server once again (and ExpressJs detects the file).
Is there a way to register new routes while the
NodeJs (ExpressJs) is running?
I tried to do the following things with no luck:
app.js
This works if the server is restarted. I tried to include this library (express-dynamic-router, but not working at runtime.)
//this is dynamic routing function
function handleDynamicRoutes(req,res,next) {
var path = req.path; //http://localhost:8080/api/rule_test
//LoadModules(path)
var controllerPath = path.replace("/api/", "./dynamicRoutes/");
var dynamicController = require(controllerPath);
dynamicRouter.index(dynamicController[req.method]).register(app);
dynamicController[req.method] = function(req, res) {
//invocation
}
next();
}
app.all('*', handleDynamicRoutes);
Finally, I readed this article (#NodeJS / #ExpressJS: Adding routes dynamically at runtime), but I couldn't figure out how this can help me.
I believe that this could be possible somehow, but I feel a bit lost. Anyone knows how can I achieve this? I'm getting a CANNOT GET error, after each file creation.
Disclaimer: please know that it is considered as bad design in terms of stability and security to allow the user or even administrator to inject executable code via web forms. Treat this thread as academic discussion and don't use this code in production!
Look at this simple example which adds new route in runtime:
app.get('/subpage', (req, res) => res.send('Hello subpage'))
So basically new route is being registered when app.get is called, no need to walk through routes directory.
All you need to do is simply load your newly created module and pass your app to module.exports function to register new routes. I guess this one-liner should work just fine (not tested):
require('path/to/new/module')(app)
Is req.params enough for you?
app.get('/basebath/:path, (req,res) => {
const content = require('content/' + req.params.path);
res.send(content);
});
So the user can enter whatever after /basepath, for example
http://www.mywebsite.com/basepath/bergur
The router would then try to get the file content/bergur.js
and send it's contents.

why is my express API not responding?

Background
I am testing a simple Hello World app using NodeJs v7 and express in cloud9.
I am trying to make my example work but I am failing.
Problem
All my cloud9 configurations are fine so that is not the problem. The problem is my app. When i debug, the route "api/v1/HolaBananas" never gets called and I don't know why!
Even worst, when i make the request, the browser just hangs, like it is waiting for an answer from the server that will never come!
Code
The index.js has the initialization and launch code. It is important for me that I keep this separate from api.js for modular reasons.
index.js
"use strict";
const express = require("express");
const app = express();
app.use("/api/v1", require("./api.js"));
app.listen(process.env.PORT);
console.log(`Server listening on port ${process.env.PORT}!`);
The api.js simply contains the routes and what they are supposed to do.
api.js
"use strict";
const express = require("express");
module.exports = function() {
const api = express.Router();
api.get("/HolaBananas", function(req, res){
res.send("hello bananas!");
});
return api;
};
Question
I am sure I am not using api.get in the right way, but I truly want to separate my initialization and launch code from the api.
How can I fix my code so it works?
Note
I am following the course
https://www.edx.org/course/introduction-mongodb-using-mean-stack-mongodbx-m101x-0
You can fix it by two following ways
var api = require("./api.js")();
app.use("/api/v1", require("./api.js"));
as API.js return a function reference. so you need to call that function to access route.
Or You need to modify your api.js as follows if you don't want to change index.js
"use strict";
const express = require("express");
const api = express.Router();
api.get("/HolaBananas", function(req, res){
res.send("hello bananas!");
});
module.exports = api;
Solution
While Vikash Sharma's solution would work, I figured it out by following the recommendation of Jayant Patil and to read this other question:
https://codereview.stackexchange.com/questions/51614/exporting-routes-in-node-js-express-4
Turns out we had the same issue, but the given answer to that question also allows me to encapsulate the api file completely inside a function, preserving its scope.
Still, kudos++ for trying!
There is one subtle thing about it: You have to invoke the function that you export in your api.js and use the router object that is returned from that function, so here is what you should do:
You have to replace this line:
app.use("/api/v1", require("./api.js"));
with this:
app.use("/api/v1", require("./api.js")());

Router.use() requires middleware functions

I use express to serve angular html file. I want to test if angular can get data from express backend.
app.component.html
<h1>
{{title}}
</h1>
<a class="button" href="/Books/getall">Get all books</a>
server.js
const express = require('express');
var app = express();
var staticRoot = __dirname;
app.set('port', (process.env.PORT || 3000));
var bookRouter = require('./bookController');
app.use(express.static(staticRoot));
app.get('/', function(req, res) {
res.sendFile('index.html');
});
app.use('/Books', bookRouter);
app.listen(app.get('port'), function() {
console.log('app running on port', app.get('port'));
});
bookController.js
var express = require('express');
var mongoose = require('mongoose');
var Books = require('./books.model');
var bookRouter = express.Router();
var router = function(){
bookRouter.use('/getall')
.get(function(req, res){
var url = 'mongodb://admin:password#ds019076.mlab.com:19076/booksdbtest';
mongoose.connect(url, function(err){
Books.find()
.exec(function(err, results){
res.send(results);
mongoose.disconnect();
});
});
});
};
module.exports = router;
But I got some error like I mentioned on the title. I have read some articles and discussions that says i need some proxy when using express and angular 2 together but I really don't know how to implement that. Can someone help me here on what is wrong with the code? That would be so much appreciated.
EDIT:
I have found what caused the error. I have changed bookRouter.use -> bookRouter.route and the error is gone. Now another problem appears. If I click the link it will load continously and nothing happens. Anyone know why?
If you are using Angular2 with Express you need to hand off the front end routes to Angular2 and then use Express for the back end points.
In most cases you will receive problems not using some sort of template engine. I like to use Express-Handlebars.
npm install hbs
Once you have this installed you can set it up in your app.js file. The file you set Express up in.
// view engine setup
app.set('views', path.join(__dirname, 'public'));
app.set('view engine', 'hbs');
Also make sure you have a public directory. If not create one.
/public
Then in the app.js
app.use(express.static(path.join(__dirname, '/public')));
Now you can put css, js, or images in this directory and call them inside your app.
Also inside the public directory you can put your index.hbs file.
Express-Handlebars uses hbs files. In our case you are only going to need one.
This is the file you would need to put the head of your html file in. As well as the
<my-app>Loading...</my-app>
We will pass this off to Angular2.
Now that we have that setup lets make a route to pass it off.
/routes/index.js
var express = require('express');
var router = express.Router();
// pass front end off to Angular2
router.get('/', function(req, res, next) {
res.render('index');
});
module.exports = router;
So now when the app loads it will load that index page. So that is the only route we need for now to pass the front end off. At this point we will configure angular2.
Webpack
What is Webpack?
Webpack is a powerful module bundler. A bundle is a JavaScript file
that incorporate assets that belong together and should be served to
the client in a response to a single file request. A bundle can
include JavaScript, CSS styles, HTML, and almost any other kind of
file.
I do not know how you have included Angular2 into your project. But if you have not set it up yet and do not know how I suggest using Webpack
Follow this tutorial to get it setup. Sometimes it is easier to start over.
Angular2
If you go into your Angular2 application now which for me is at
/assets/app/
You will set up your components and their own routing. Any links created for the front views will be done here now. So in the /assets/app directory you will need a app.routing.ts file.
Routing in Angular2
There are many ways to build this file. I like to use templates then child templates which would be to much to explain in this answer. For you though a basic routing file will look like this.
import { Routes, RouterModule } from "#angular/router";
import { AboutComponent } from './about.component';
import { HomeComponent } from './home.component';
const APP_ROUTES: Routes = [
{ path: '', redirectTo: 'home', pathMatch: 'full' },
{ path: 'home', component: HomeComponent },
{ path: 'about', component: AboutComponent },
];
export const routing = RouterModule.forRoot(APP_ROUTES);
Links
Now that you have routes setup in Angular2 you can create links in your html files or home.component.html like this,
<li class="nav-item p-x-1">
<a class="nav-link" routerLinkActive="active" [routerLink]="['/home']"> Home</a>
</li>
I realize this has not basically given you code that will compile which in a perfect world we would all share like that. But I really understand the struggle and want to help. There is just to much to explain to be able to hand you over that much and it make sense. Please use the tutorial I gave you above. If you have any questions ask. Try and use my answer as a baseline to understand the different components of putting together an application like this.
Disclaimer
You do not have to use Express-Handlebars. If you find in that tutorial they are doing something different just follow along so you can get a working app you understand. I like hbs because I only use it for one file then handle everything on the front end with Angular2. The reason I use it instead of just an html file is because it is a huge pain to configure Express to work right with Angular2. For example if you just use an html file then add it to the public directory then run your app and refresh the page you will get a 404 error. Unless you add something like,
HashLocationStrategy
Which adds ugly hash tags to your url and is annoying in a whole different way. Using a template engine for that small task makes everything work so much easier and better. The configuration is minimal as you can see above.
For a seed project I use,
git clone https://github.com/wuno/md-recruit.git
in the terminal navigate to the new directory you just cloned.
type,
webpack
Then
npm start
Then navigate to localhost:3000
Make sure you have webpack installed globally
npm install webpack -g
It is very important that what ever version of webpack is installed locally in your project has to match the global install.

Access Previously-Defined Middleware

Is there a way to access or delete middleware in connect or express that you already defined on the same instance? I have noticed that under koa you can do this, but we are not going to use koa yet because it is so new, so I am trying to do the same thing in express. I also noticed that it is possible with connect, with somewhat more complicated output, but connect does not have all the features I want, even with middleware.
var express = require('express');
var connect = require('connect');
var koa = require('koa');
var server1 = express();
var server2 = connect();
var server3 = koa();
server1.use(function express(req, res, next) {
console.log('Hello from express!');
});
server2.use(function connect(req, res, next) {
console.log('Hello from connect!');
});
server3.use(function* koa(next) {
console.log('Hello from koa!');
});
console.log(server1.middleware);
// logs 'undefined'
console.log(server2.middleware);
// logs 'undefined'
console.log(server2.stack);
logs [ { route: '', handle: [Function: connect] } ]
console.log(server3.middleware);
// logs [ [Function: koa] ]
koa's docs say that it added some sugar to its middleware, but never explicitly mentions any sugar, and in particular does not mention this behavior.
So is this possible in express? If it is not possible with the vanilla version, how hard would it be to implement? I would like to avoid modifying the library itself. Also, what are the drawbacks for doing this, in any of the 3 libraries?
EDIT:
My use case is that I am essentially re-engineering gulp-webserver, with some improvements, as that plugin, and all others like it, are blacklisted. gulp is a task runner, that has the concept of "file objects", and it is possible to access their contents and path, so I basically want to serve each file statically when the user goes to a corresponding URL in the browser. The trouble is watching, as I need to ensure that the user gets the new file, and not the old version. If I just add an app.use each time, the server would see the file as it is originally, and never get to the middleware with the new version.
I don't want to restart the server every time a file changes, though I will if I can find no better way, so it seems I need to either modify the original middleware on the fly (not a good idea), delete it, or add it to the beginning instead of the end. Either way, I first need to know where it "lives".
You might be able to find what your looking for in server1._router.stack, but it's not clear what exactly you're trying to do (what do you mean "access"?).
In any case, it's not a good idea to do any of these, since that relies strictly on implementation, and not on specification / API. As a result any and all assumptions made regarding the inner implementation of a library is eventually bound to break. You will eventually have to either rewrite your code (and "reverse engineer" the library again to do so), or lock yourself to a specific library version which will result in stale code, with potential bugs and vulnerabilities and no new features / improvements.

accessing required modules from other modules

I have a bare-bone express application, exactly the one that is created with the express command.
I have installed socket.io and attached it to my server, like this:
var app = express(),
server = http.createServer(app),
io = io.listen(server);
server.listen(8000);
Now, I also have the routes files, which is called like this:
app.get('/', routes.index);
Inside this module I have the following function:
exports.index = function(req, res){
socket.emit('news', { message: "foo" });
};
This obviously leads to a 500 reference error, because the routes file is an exportable module, and obviously has no idea what the socket is, as it is located in the app.js file.
Is there a way I can access this socket object from this, or any other file? Please note that it is attached to the express generated app. Here is a link to said project: http://jsfiddle.net/E27yN
extra: what about getting/setting session data?
Thanks in advance.
If I'm reading this correctly, I had a very similar problem: Handling Node.js Async Returns with "require" (Node ORM)
The way I resolved it was by putting a function call to the require, and returning that function in my exports, then that was accessible via that local variable. In your case, I think it'd be something like this:
var routes = require("routes.js")(io);
console.log(routes.index()); // will log return of the function "index" in routes.js.
// in routes.js
module.exports = function(io) {
var exports = this;
exports.index = function(req,res,io) {
// now io.socket is available to routes
console.log(io);
}
return exports;
}
Now you can access whatever you've exported by using that variable. So getting and setting session data would be a matter of getting that info to/from the proper place and modifying it in your parent file (usually whatever you're launching with node, like app.js or what have you).
Hope this helps!
EDIT: After discussing this in the comments, I think the problem is you're trying to use express and socket.io on the same port.
The example they give in your link (http://socket.io/#how-to-use) actually shows that they are serving up index.html from app.get("/"). Index.html includes the socket.io script file () which is by default served up when you start your socket server.
That would look like this jsfiddle: http://jsfiddle.net/Lytpx/
Note that you serve up the index.html page through express, but your socket service actually serves up /socket.io.js which then makes connection calls to the listening socket service (note, you may want to change localhost in your io.connect call in index.html to whatever your server is).

Resources