Parent root folder domain in express.js - javascript

The way I desgined my express nodejs application as per below;
app.get('/', page.index);
//Add new,edit and delete form
app.get('/approval', page.approval);
//Task to approve/reject the subordinate form
app.get('/task', page.task);
//Report
app.get('/report', page.report);
//Admin
app.get('/admin', page.admin)
app.listen(3000);
Hence I can always access using the url i.e
http://<Servername>:3000/
http://<Servername>:3000/approval
http://<Servername>:3000/task
Lets say now I will need to have a parent app root as appsA ( ie.
http://<Servername>:3000/appsA/
http://<Servername>:3000/appsA/approval
http://<Servername>:3000/appsA/task
How can I do that from express/nodejs without go and add appsA as a url on each of the get request.

You can use router to do that, example
var express = require("express");
var router = express.Router();
router.get('/', page.index);
router.get('/approval', page.approval);
router.get('/task', page.task);
router.get('/report', page.report);
router.get('/admin', page.admin)
// Then implement root route path
app.use("/appsA", router);
Note: Routers are only available for Express 4 and above, This solution will not work on all Express versions below version 4, If you still want to do this with express 3, you can implement your own Router middleware

Related

Express routing - prevent app.use('/') top-level path middleware from executing when visiting child/nested paths directly

So I'm not really sure if the title is descriptive enough, but here is a super simple example.
My site has a public area and a restricted admin area.
example.com/admin (admin home page)
example.com/admin/news (news page)
example.com/admin/posts (posts page)
And because I don't want people who aren't administrators or logged in to be able to access it, I have a simple middleware function to check for cookies.
app.js
const express = require('express');
const app = express();
const authMiddleWere = async (req, res, next) => {
// pseudo-code, do some cookie validity check here
console.log(`Route: ${req.url}`)
if (cookie) {
next();
}
};
const adminRouter = require('./routes/private/home');
const newsRouter = require('./routes/private/news');
const postsRouter = require('./routes/private/posts');
app.use('/admin/', authMiddleWere, adminRouter);
app.use('/admin/news', authMiddleWere, newsRouter);
app.use('/admin/posts', authMiddleWere, postsRouter);
/routes/private/home.js
const express = require('express');
const router = express.Router();
router.get('/', async (req, res, err) => {
res.render('private/home');
});
module.exports = router;
The problem here is that this authMiddleWere function gets called twice when I visit nested paths such as example.com/admin/news which shares the same pattern - it's starting with /admin/......
I can tell that for sure because we are logging the req.url in our middleware function so if I go to example.com/admin it will log out:
Route: /
But if I go to example.com/admin/news it will log out both:
Route: /
Route: /news
So what is causing this and how do I work my way around it? I'm assuming that what I described is the intended behavior of Express.js so I am looking for a way to get around this or (re)structure my code better.
Cheers!
You can use a regex for your route.
app.use(/\/admin$/, authMiddlewear, authRouter);
This will match only routes that end in admin. You may need to handle cases where the route is /admin/ instead of /admin, but iirc, express handles that intelligently.
Well one way you can fix this is by creating a separate route file and splitting everything into a MVC manner. For example:
Inside your main app.js just create a route pointing to the /admin like so:
app.use('/admin', authMiddleWere, require('./src/your-route-to-the-file/admin.route'));
Inside the admin.route file, call your controller like this:
const express = require("express");
const router = express.Router();
const mainAdminCtrl = require("../controllers/admin.controller");
router.get("/news", mainAdminCtrl.adminAuthDisplay);
module.exports = router;
Where the const mainAdminCtrl is your controller and the function adminAuthDisplay is your service.
Essentially, you are splitting your functionality in to a dedicated router, controller and service file. So when you try to access the route /admin, it will look for any suffix inside the router file.
In a case where you want to access the /news endpoint, your API will only make the call once.
If this helps, I can expand my explanation further.

Linking Socket IO to Express routes

I'm been looking around everywhere for the last few days for a way to access my socket IO instance running in Express from my routes and have not found a working solution.
The problem here is that I only run my socket IO instance at run-time and my routes and defined before that obviously.
The most promising solution I have found is one that wraps my routes in a function and requires it in my app.js file with whilst passing in the IO instance as an argument like so var routes = require('routes')(io)
Here is my setup:
app.js
const express = require('express');
const app = express();
const server = require('http').Server(app);
const io = require('socket.io')(server);
io.on('connection', function(client) {
console.log(io.sockets.sockets)
});
app.set('port', 7777);
const routes = require('./routes/index')(io);
app.use('/', routes);
app.listen('7777');
routes.js
module.exports = function(io) {
const express = require('express');
const router = express.Router();
router.get('/test', (req, res) => {
console.log(io.sockets.sockets);
});
return router;
}
If I connect to the WebSocket, my io.on event fires and I get a console log of the connected sockets in the form of a fairly large object that contains the socket id's etc.
If however, I get to the route '/test' I get a console log of a blank object: {}. I would imagine this is because the instance of the socket I am passing down to the routes does not have anyone connected to it at the time hence the blank object is returned, but this is just my best guess.
Where I'm a little stuck is how to get the full instance with all live connections to the route.
Alternatively, I've thought of attaching the io instance to the request object like so in order to have access to it in the routes:
server.on('request', function(req, res){
req.io = io;
}
but couldn't quite get it to work, nor am I sure this is the correct approach.
I imagine this must be a common issue so I would love a clear answer on how to work around this and the correct approach to tackle the issue.
EDIT
So I eventually got my above code working, I hit the '/test' endpoint from an AJAX GET request within the chrome extension instead of my just visiting the URL localhost:7777/test. What I can't understand here is why it works with an AJAX request but not page navigation? (The page navigation is done after I make the socket connection in the extension)

node how can My app find index.js

Folder structure
bin - www.js
lib - jsFiles...
models - jsFiles...
node_modules -Folders and Files
public - index.html
route - jsFiles...
index.js
package.json
I use Express, angular.js. Server starts at www.js and It calls
index.js. After that, When I type merely "localhost:3000" It shows me
public/index.html. I don't have route for '/' but It shows me
'public/index.html'. I can not understand this. Please let me know
about the process.
www.js
var debug = require('debug')('example-server');
var app = require(process.cwd()+'/index');
//listen at 3000 port
app.set('port',process.env.PORT || 3000);
var server = app.listen(app.get('port'),function()
{
debug('Express server listening on port ' + server.address().port);
});
index.js
var favicon = require('serve-favicon');
var express = require('express');
var path = require('path');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
//Connection for DB
require('./lib/connection');
var employees = require('./routes/employees');
var teams = require('./routes/teams');
var app = express();
// Writing at routing table
app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended:true }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname,'public')));
app.use(employees);
app.use(teams);
// send 404 to errorhandler
app.use(function(err,req,res,next)
{
var err = new Error('Not Found');
err.status = 404;
console.log(error);
next(error);
});
...
...
module.exports = app;
In express.js the sequence in which you register your middleware makes a huge difference.
When express.js receives a request, it starts from top and executes registered middleware.
Middlewares are registered in express app using app.use(middleware_goes_here) this type of middleware gets executed no matter what the request url is on the other hand you can also register a middleware like app.use('/url/path',middleware_goes_here) in this case we are registering this middleware to '/url/path' so this middleware will only get executed when you visit '/url/path' (and non of the previous matching middleware serves the request without calling next() )
This app.use(express.static(path.join(__dirname,'public'))); line of code does the magic.
You can go here (express.static ref) to know more about static content serving and routing.
Basically what happens is, we are configuring express.static middleware to serve static content "as is" from "public" folder. So when you make any request and it matches a static content in public folder, then it will serve it otherwise express.static will call next middleware in sequence.
So in your case, the first middleware that actually server input request is express.static without any route filters, so it servers index.html even without a specifically defined route. If your public folder had file at public/javascript/jquery.js then following url will map to it http://localhost:3000/javascript/jquery.js
NOTE: You do not have to specify "public" in the url, the way in which express.static is registered, it will server contents FROM "public" folder.
................
UPDATE: How does default work in express.static?
By default, app.use(express.static(path.join(__dirname,'public'))); this will take index.html as default document. If you want to set index2.html as your default document, you can do that by doing something like this app.use(express.static(path.join(__dirname,'public'),{index: 'index2.html'}));
Hope it helps.
Put a relative path to folder(one up in hierarchy).
var app = require('../index');

Consistent url routing on client side React app served from Express

I am currently using Express to load a one page client side app in React.js and react-router.
It is structured such that I have /about and /contact as standard jade views, but /ui as the actual react app. Any subsequent navigation beyond /ui such as ui/dashboard is handled by react-router. This means that if I need to visit www.foobar.com/ui/dashboard, I would need to hit my route for ui*, grab the url past ui/ (dashboard in this case) and pass that to react router (via variable in the jade view which is the react app's entry point) which then loads this route from a react component. I would like to make this approach work as this enables users to save urls for the react app. Trouble is I can't figure out how this could work as:
I cannot pass variables with express router redirects
I need to load the react app without anything beyond /ui as otherwise react router will append its routing urls in front of it ruining the point in this approach
I cannot store the initial request url in a cookie and then redirect as I need to send a response in order to set a cookie
I cannot satisfactorily modify the url via client side js
Code:
//Example 1
router.get('/ui*', requireLogin, function(req, res){
res.render('./app/index', {initURL: req.path); //Doesnt work as everything past ui/ is still present when react-router does its thing
});
//Example 2
router.get('/ui', requireLogin, function(req, res){
res.render('./app/index', {initURL: req.path); //Doesnt work as no way of accessing the initial requests url when redirecting
});
router.get('/ui*', requireLogin, function(req, res){
res.redirect('/ui'); //Cant pass additional parameters here
});
var express = require('express')
var React = require('react')
var Router = require('react-router')
var routes = require('./routes')
var app = express()
app.use('/ui', requireLogin, function(req, res, next) {
// store initially requested url as a query parameter
// e.g. /ui/dashboard would become /ui?redirect=dashboard
// if the user is not logged in
var params = req.query;
// params.redirect === 'dashboard'
// Now you can check if there is a redirect and use it in your initial route
var router = Router.create({location: req.url, routes: routes})
router.run(function(Handler, state) {
var html = React.renderToString(<Handler/>, {initalState: params.redirect})
return res.render('react_page', {html: html})
})
})
Does this get you on the right track?
I think you have to distinguish your client side routing and your server side routing.
You will need a server side routing:
the client needs to load the static content (*.html, *.js, *.css)
the client needs to authenticate, and load the data he needs (/api/...)
You will also need a client side routing framework (like ui.router for angular.js):
you will split your client application logically in different routes that the browser (user) can remember, like /, /dashboard, /...
Regards, Remy

Can I add multiple modules to nodeJS?

I am new to NodeJS. What I wanted to know is, can I like call 2 JS files using NodeJS & ExpressJS. Basically I want to have 2 seperate files so I can work on one and my partner can work on another one. So I want Server.js to call one file which contains some part of my REST API and other one contains rest of the function.
|--NodeModules[etc..]
|--Server.js
|--Rest/
| |--RestAPI1.js
| |--RestAPI2.js
It will be really helpful in the development of my project, if this is possible.
You can define routes in different files like this:
Server.js
var express = require('express')
var router1 = require("./routers/router1");
var app = express();
.....
app.use("/user", router1);
Router1.js
var router = require("express").Router();
router.get("/", function(req, res) {
...
});
module.exports = router;

Categories

Resources