Dynamically load routes with express.js - node.js

I am using express.js as a webserver and would like an easy way to separate all the "app.get" and "app.post" functions to separate files. For example, if I would like to specify get and post functions for a login page, I would like to have a login.js file in a routes folder that is dynamically loaded (will automatically add all of the files without having to specify each one) when I run node app.js
I have tried this this solution!, but it isn't working for me.

app.js
var express=require("express");
var app=express();
var fs=require("fs");
var routePath="./routers/"; //add one folder then put your route files there my router folder name is routers
fs.readdirSync(routePath).forEach(function(file) {
var route=routePath+file;
require(route)(app);
});
app.listen(9123);
I have put below two routers in that folder
route1.js
module.exports=function(app){
app.get('/',function(req,res){
res.send('/ called successfully...');
});
}
route2.js
module.exports=function(app){
app.get('/upload',function(req,res){
res.send('/upload called successfully...');
});
}

Typescript
routes/testroute.ts
import { Router } from 'express';
const router = Router();
router.get('/test',() => {
// Do your stuffs Here
});
export = router;
index.ts
let app = express()
const routePath = path.join(__dirname, 'routes');
fs.readdirSync(routePath).forEach(async (filename) => {
let route = path.join(routePath, filename);
try {
const item = await import(route);
app.use('/api', item.default);
} catch (error) {
console.log(error.message);
}
});
app.listen()

I ended up using a recursive approach to keep the code readable and asynchronous:
// routes
processRoutePath(__dirname + "/routes");
function processRoutePath(route_path) {
fs.readdirSync(route_path).forEach(function(file) {
var filepath = route_path + '/' + file;
fs.stat(filepath, function(err,stat) {
if (stat.isDirectory()) {
processRoutePath(filepath);
} else {
console.info('Loading route: ' + filepath);
require(filepath)(app, passport);
}
});
});
}
This could be made more robust by checking fro correct file extensions etc, but I keep my routes folder clean and did not want the added complexity

With this approach, there is no need to write routes manually. Just setup a directory structure like the URL paths. Example route is at /routes/user/table/table.get.js and API route will be /user/table.
import app from './app'
import fs from 'fs-readdir-recursive'
import each from 'lodash/each'
import nth from 'lodash/nth'
import join from 'lodash/join'
import initial from 'lodash/initial'
const routes = fs(`${__dirname}/routes`)
each(routes, route => {
let paths = route.split('/')
// An entity has several HTTP verbs
let entity = `/api/${join(initial(paths), '/')}`
// The action contains a HTTP verb
let action = nth(paths, -1)
// Remove the last element to correctly apply action
paths.pop()
action = `./routes/${join(paths, '/')}/${action.slice(0, -3)}`
app.use(entity, require(action))
})
Example route:
import { Router } from 'express'
import Table from '#models/table.model'
const routes = Router()
routes.get('/', (req, res, next) => {
Table
.find({user: userIdentifier})
.select('-user')
.lean()
.then(table => res.json(table))
.catch(error => next(error))
})
module.exports = routes

Related

How do I get the full route path including the parameters from express or extend the request object to do so?

I have the following route in my express (version 4.17.1) API in a postTimecardCompany.js file:
const mongoose = require('mongoose');
const Timecard = require('./../models/timecard');
function postTimecardCompany(server) {
server.post('/api/v1/timecard/:userId', (req, res) => {
// Insert timecard data into the database
Timecard.create(data, (error, result) => {
// Check for errors
if (error) {
res.status(500).end();
return;
}
// Respond
res.status(200).send({timecardId: result._id});
});
});
}
module.exports = postTimecardCompany;
The route (among other routes) is loaded via the following mechanism by server.js file:
[
'postTimecardCompany',
'anotherRoute',
'someOtherRoute',
'andSoOn...'
].map((route) => {
require('./core/routes/' + route + '.js').call(null, server)
});
I have a middleware (in server.js file) where I check which route is being called.
server.use((req, res, next) => {
// If route is "/api/v1/timecard/:userId" do something
});
I have found various solutions which do nearly what I am looking for, but not exactly.
For example, if I post to the route with a data parameter userId value of "123f9b" then req.originalUrl gives an output of "/api/v1/timecard/123f9b."
What i'm looking to get is the original route path with the parameters in it so for a request of "/api/v1/timecard/123f9b" it would be: "/api/v1/timecard/:userId."
How do I get this functionality in express or extend express to get the original route path with parameters in the request object?
if you want to use from your approach, it's is impossible, after that your approach is not standard in express check the documentation, if you want get routes in a middleware you should try like this:
server.js
const express = require('express')
const server = express()
const postTimecardCompany = require('./routes/postTimecardCompany.js')// don't use map()
server.use("/",postTimecardCompany)//use the routes
server.listen(6565,()=>console.log(`Listening to PORT 6565`))
routes of postTimecardCompany.js
use Router of express and export router, and you can use middleware before each route you want, there are many ways to use middleware in routes, check the documentation
const express = require("express");
const router = express.Router();
const middleware = require('../middleware');//require middlewares
router.post("/api/v1/timecard/:userId", middleware,(req, res) => {
// Insert timecard data into the database
console.log(req.route.path);
});
module.exports = router;
middleware.js
module.exports = ((req, res, next) => {
console.log(req.route.path);
next()
});

require is not define / module.exports is not define with node.js and Express

After receveing help about using Express, I continued to follow tutorials about Node.js. I'm at a point where i'm building my own routes in controllers to create a REST API. I have two files, app.js and /controllers/account-api.js.
Here's my app.js shortened (i deleted the parts that were not used my my test), and the line that is returning me some issues.
import express from 'express';
import exphbs from 'express-handlebars';
import * as accountApiRoutes from './controllers/account-controller.js';
import * as bodyParser from 'body-parser';
var app = express();
app.engine('handlebars', exphbs());
app.set('view engine', 'handlebars');
app.use(bodyParser.urlencoded({extended:true}));
app.use(accountApiRoutes); // ISSUE HERE, i tried ('/account-api', accountApiRoutes) too
app.get('/', function(req, res)
{
res.redirect('/server-home');
});
app.get('/server-home', function(req, res)
{
res.render('server-home');
});
app.listen(1337, '127.0.0.1');
console.log('Express Server running at http://127.0.0.1:1337/');
And here's my ./controllers/account-api.js shortened again to give the main elements that causes the issue :
import express from 'express';
const apiRouter = express.Router(); //ISSUE HERE
var accounts = [];
accounts.push( { code: 1, name: 'Pierrette', adress: 'Sur la Lune'} );
// =========== API ROUTES =========== //
// GET
apiRouter.route('/produit-api/produit/:code')
.get( function(req, res, next) {
var codeSended = req.params.code;
var account = findAccountInArrayByCode(codeSended);
res.send(account);
});
// =========== METHODS AND FUNCTIONS =========== //
// GET
function findAllAccounts() {
return accounts;
}
function findAccountInArrayByCode(codeSended) {
var accountFound = null;
for(i in accounts)
{
if(accounts[i].code === codeSended)
{
accountFound = accounts[i];
break;
}
}
return accountFound;
}
module.exports = { //ISSUE HERE
getApiRouter: function() {
const apiRouteur = express.Router();
return apiRouter;
}
}
The problem is.. This code returns me "module" is not defined.
I use Node.JS with Express and Handlebars.
For what I saw online, when using "app.use", it requires a function. And module.exports too. I tried various solutions, like this one :
account-api.js
const apiRouter = function() { return express.Router() }
...
module.exports = apiRouteur;
The problem is that it changes the type of apiRouteur, when calling apiRouteur.get from IRouter to () => Router, and the routes break.
I don't know how to arrange the code to make the module.exports returning a function that works, or if the problem is not even about the type of value returned, but if I'm missing dependancies, etc...
Thanks for your help.
EDIT : With the explanations I got, I replaced all my ES6 calls to commonjs imports. But it doesn't solve the problem. Now it's "require" that's not define.
I was stuck firstly by "require is not defined", and the solution I was given by reading old SO threads about it, the answer was regularly to use ES6 imports...
ack to the begining I guess ! Maybe I miss something in my project?
Your problem is this line app.use(accountApiRoutes); and you are using a mix of ES6 and commonjs modules.
To fix the module imports (as you are using .js files not .mjs) change all your ES6 imports i.e import * as xyz imports to commonjs imports const x = require('...');
The accountApiRoutes is an object but not a Router object.
To fix you just need to pass the router object to the app.use function.
So you will need to make a couple of changes based on what you have supplied above.
// ./controllers/account-api.js
const express = require('express');
...
module.exports = { //ISSUE HERE
getApiRouter: function() {
return apiRouter; // you have already defined the router you don't need to recreate it
}
}
Properly pass the Router object to the express app.
const express = require('express');
const exphbs = require('express-handlebars');
const bodyParser = require('body-parser');
const accountApiRoutes = require('./controllers/account-controller.js');
...
app.use(accountApiRoutes.getApiRouter());
You could also just set module.exports to your configured router in your account-api.js and then you could pass it directly to app.use as you have already done in your server above. Either way should work. To can do that as follows:
// ./controllers/account-api.js
const express = require('express');
...
module.exports = apiRouter;
And in your server.js
const accountRouter = require('./controllers/account-controller.js');
app.use(accountRouter);

Express homepage not rendered on server

I am developing a react app that uses server-side rendering.
My routes work, except the homepage. Not sure why. I'm assuming I'm not setting up my express server correctly to handle the index route...?
The following is the module which handles express' setup and whatnot. I'm assuming I have the app.get('*) or app.use(express.static) incorrect.
app.js (server)
require('ignore-styles')
const compression = require('compression')
const express = require('express')
const path = require('path')
require('babel-register')({
ignore: /\/(build|node_modules)\//,
presets: ['env', 'react-app']
})
const universalLoader = require('./universal')
const app = express()
// Support Gzip
app.use(compression())
// Serve static assets
app.use(express.static(path.resolve(__dirname, '..', 'build')))
// Always return the main index.html, so react-router render the route in the client
app.get('*', universalLoader)
module.exports = app
universalLoader.js (server)
import path from 'path'
import fs from 'fs'
import React from 'react'
import { Provider } from 'react-redux'
import { renderToString } from 'react-dom/server'
import { StaticRouter, matchPath } from 'react-router-dom'
import configureStore from '../src/store'
import App from '../src/components/App'
import routes from '../src/shared/routes'
import { getSiteInfo } from '../src/store/actions/siteInfo'
import { REACT_APP_SITE_KEY } from '../src/shared/vars'
import Helmet from 'react-helmet'
module.exports = function universalLoader(req, res, next) {
// console.log('Loading....')
const store = configureStore()
const fullUrl = req.protocol + '://' + req.get('host') + req.originalUrl
let routeFound = false
// Try to find a matched route
const promises = routes.reduce((promise, route) => {
var props = matchPath(req.url, route)
// If route was matched, component exists, and has an initialAction
// then call it's initialAction.
// This action will most-likely load some data asyncronously
if (props && route.component && route.component.initialAction) {
promise.push(Promise.resolve(store.dispatch(route.component.initialAction(store, props))))
}
return promise
}, [])
// Load initial site data
promises.push(Promise.resolve(store.dispatch(getSiteInfo(REACT_APP_SITE_KEY))))
// Wait until all async data has been loaded
Promise.all(promises)
.then(() => {
// Load index file path
const filePath = path.resolve(__dirname, '..', 'build', 'index.html')
// Read index file
fs.readFile(filePath, 'utf8', (err, htmlData) => {
if(err){
console.error('read err', err)
return res.status(404).end()
}
const preloadedState = store.getState()
// console.log("PreloadedState:", preloadedState)
const context = preloadedState
// console.log(context)
// Note: Pass in serverRequest prop so the App knows the domain it's on for meta tags
const markup = renderToString(
<Provider store={store}>
<StaticRouter location={req.url} context={context}>
<App serverRequest={req} serverResponse={res} />
</StaticRouter>
</Provider>
)
const helmet = Helmet.renderStatic()
// Somewhere a `<Redirect>` was rendered
if(context.url){
console.log('Redirected:', context.url)
redirect(301, context.url)
// we're good, send the response
}else{
// Page meta data
const meta = helmet.title.toString() + helmet.meta.toString() + helmet.link.toString()
// Prep state to be injected into DOM for client
const pageState = `<script>window.__PRELOADED_STATE__ = ${JSON.stringify(preloadedState).replace(/</g, '\\u003c')}</script>`
// Inject state and markup
const RenderedApp = htmlData
.replace('<script></script>', pageState) // Store state to pass to client
.replace('<meta name="helmet">', meta) // Meta data
.replace('{{SSR}}', markup) // Actual markup/component html
console.log("SSR Rendered: ", req.path)
res.send(RenderedApp)
}
})
})
.catch(err => {
console.log("Error:", err)
})
}
I am console.log()-ing when a route is being handled within universalLoader(). All routes show in the console that stuff is happening. Except my homepage. It does not even show the "Loading..." message.
express.static will be serving up any files in your build directory. If it finds the requested file it will serve it up and end the request/response. No middleware registered after express.static will get the chance to run if a suitable file is found.
Based on this line:
const filePath = path.resolve(__dirname, '..', 'build', 'index.html')
it would appear that you have a file called index.html in your build directory. This will get served up by express.static when you hit the URL for index.html but it will also get served up if you just hit the / URL because express.static defaults to serving up index.html. See the index property here:
https://expressjs.com/en/4x/api.html#express.static
The directory you point express.static at needs to contain files that are static, i.e. that require no processing whatsoever. If any of the files need processing they need to live elsewhere. Note the parallels with how an Express app typically has a separate folder for views, which in many ways is similar to what you're trying to do.
I would also suggest commenting out express.static to see what effect that has. It should be a quick way to confirm that express.static is responsible for stopping your index route being reached.
Update
Based on your comments it would seem that you do have a static directory at build/static that contains your JS and CSS files. You can serve this up directly using:
app.use(express.static(path.resolve(__dirname, '..', 'build', 'static')))
However, this will cause all your URLs to change too, so http://localhost/static/js/example.js will now be http://localhost/js/example.js. To retain the original URLs you would need to put the static back in via the route path:
app.use('/static', express.static(path.resolve(__dirname, '..', 'build', 'static')))

Use node js domains in handler function

I have https server written in express js. And I added domains to my server. App.js file:
var d = require('domain').create();
d.on('error', function(error) {
console.error("Domain caught error: "+ error.stack);
});
d.run(function() {
var express = require('express');
var appServer = express();
var https = require('https').createServer(options, appServer);
https.listen(8000, function() {
log.info('Server is listening on port ' + 8000);
});
appServer.use(appServer.router);
var routes = require('./routes')(appServer); //my routes file
});
I have route handler functions in other files. How can I use domain created in my app.js file in my route files without exporting it from app.js file.
Update:
routes.js file:
var auth = require('./auth');
module.exports = function(app) {
app.namespace('/login', function(){
app.post('/user', auth.verifyUser);
});
};
auth.js file:
exports.verifyUser = function(req,res) {
//here I want to see my domain
};
You can pass it when you require your routes:
var routes = require('./routes')(appServer, d);
Then within your routes/index.js file:
module.exports = function(app, domain) {
// ...
};
Update
To update the answer based on the question update, here's a possible solution to include the domain within each route definition. There are a couple ways to do this (you could make each route a class, which would be instantiated and passed the domain, then have a function defined per route, for example). Because the intent is to keep these route definition signatures function(req, res) as you've defined above for verifyUser, we're going to need to pass the domain to this route prior to calling the function. Keeping this very simple, we can do just that with a setDomain function:
Same code in your main index.js when you require your routes:
var routes = require('./routes')(appServer, d);
In your routes.js, you can pass the domain to the routes via the setDomain function:
var auth = require('./auth');
module.exports = function(app, domain) {
auth.setDomain(domain);
app.namespace('/login', function() {
app.post('/user', auth.verifyUser);
});
};
Finally, in your auth.js file, you can get the domain, and have access to it within the scope of the file to use it in the verifyUser function:
var myDomain;
module.exports.setDomain = function(domain) {
myDomain = domain;
};
module.exports.verifyUser = function(req, res) {
res.send("myDomain: " + myDomain);
};
I'm not sure I really like this solution, but again, it's keeping the signature the same for verifyUser. Maybe you'll think of a better one, or want to refactor your code to better make use of this domain you're passing around within your code (maybe define it somewhere else and pull it from there wherever it's needed).

Global Variable in app.js accessible in routes?

How do i set a variable in app.js and have it be available in all the routes, atleast in the index.js file located in routes. using the express framework and node.js
It is actually very easy to do this using the "set" and "get" methods available on an express object.
Example as follows, say you have a variable called config with your configuration related stuff that you want to be available in other places:
In app.js:
var config = require('./config');
app.configure(function() {
...
app.set('config', config);
...
}
In routes/index.js
exports.index = function(req, res){
var config = req.app.get('config');
// config is now available
...
}
A neat way to do this is to use app.locals provided by Express itself.
Here is the documentation.
// In app.js:
app.locals.variable_you_need = 42;
// In index.js
exports.route = function(req, res){
var variable_i_needed = req.app.locals.variable_you_need;
}
To make a global variable, just declare it without the var keyword. (Generally speaking this isn't best practice, but in some cases it can be useful - just be careful as it will make the variable available everywhere.)
Here's an example from visionmedia/screenshot-app
file app.js:
/**
* Module dependencies.
*/
var express = require('express')
, stylus = require('stylus')
, redis = require('redis')
, http = require('http');
app = express();
//... require() route files
file routes/main.js
//we can now access 'app' without redeclaring it or passing it in...
/*
* GET home page.
*/
app.get('/', function(req, res, next){
res.render('index');
});
//...
To declare a global variable you need do use global object. Like global.yourVariableName. But it is not a true way. To share variables between modules try to use injection style like
someModule.js:
module.exports = function(injectedVariable) {
return {
somePublicMethod: function() {
},
anotherPublicMethod: function() {
},
};
};
app.js
var someModule = require('./someModule')(someSharedVariable);
Or you may use surrogate object to do that. Like hub.
someModule.js:
var hub = require('hub');
module.somePublicMethod = function() {
// We can use hub.db here
};
module.anotherPublicMethod = function() {
};
app.js
var hub = require('hub');
hub.db = dbConnection;
var someModule = require('./someModule');
the easiest way is to declare a global variable in your app.js, early on:
global.mySpecialVariable = "something"
then in any routes you can get it:
console.log(mySpecialVariable)
This was a helpful question, but could be more so by giving actual code examples. Even the linked article does not actually show an implementation. I, therefore, humbly submit:
In your app.js file, the top of the file:
var express = require('express')
, http = require('http')
, path = require('path');
app = express(); //IMPORTANT! define the global app variable prior to requiring routes!
var routes = require('./routes');
app.js will not have any reference to app.get() method. Leave these to be defined in the individual routes files.
routes/index.js:
require('./main');
require('./users');
and finally, an actual routes file, routes/main.js:
function index (request, response) {
response.render('index', { title: 'Express' });
}
app.get('/',index); // <-- define the routes here now, thanks to the global app variable
Here are explain well, in short:
http://www.hacksparrow.com/global-variables-in-node-js.html
So you are working with a set of Node modules, maybe a framework like Express.js, and suddenly feel the need to make some variables global. How do you make variables global in Node.js?
The most common advice to this one is to either "declare the variable without the var keyword" or "add the variable to the global object" or "add the variable to the GLOBAL object". Which one do you use?
First off, let's analyze the global object. Open a terminal, start a Node REPL (prompt).
> global.name
undefined
> global.name = 'El Capitan'
> global.name
'El Capitan'
> GLOBAL.name
'El Capitan'
> delete global.name
true
> GLOBAL.name
undefined
> name = 'El Capitan'
'El Capitan'
> global.name
'El Capitan'
> GLOBAL.name
'El Capitan'
> var name = 'Sparrow'
undefined
> global.name
'Sparrow'
My preferred way is to use circular dependencies*, which node supports
in app.js define var app = module.exports = express(); as your first order of business
Now any module required after the fact can var app = require('./app') to access it
app.js
var express = require('express');
var app = module.exports = express(); //now app.js can be required to bring app into any file
//some app/middleware, config, setup, etc, including app.use(app.router)
require('./routes'); //module.exports must be defined before this line
routes/index.js
var app = require('./app');
app.get('/', function(req, res, next) {
res.render('index');
});
//require in some other route files...each of which requires app independently
require('./user');
require('./blog');
this is pretty easy thing, but people's answers are confusing and complex at the same time.
let me show you how you can set global variable in your express app. So you can access it from any route as needed.
Let's say you want set a global variable from your main / route
router.get('/', (req, res, next) => {
req.app.locals.somethingNew = "Hi setting new global var";
});
So you'll get req.app from all the routes. and then you'll have to use the locals to set global data into. like above show you're all set. now
I will show you how to use that data
router.get('/register', (req, res, next) => {
console.log(req.app.locals.somethingNew);
});
Like above from register route you're accessing the data has been set earlier.
This is how you can get this thing working!
As others have already shared, app.set('config', config) is great for this. I just wanted to add something that I didn't see in existing answers that is quite important. A Node.js instance is shared across all requests, so while it may be very practical to share some config or router object globally, storing runtime data globally will be available across requests and users. Consider this very simple example:
var express = require('express');
var app = express();
app.get('/foo', function(req, res) {
app.set('message', "Welcome to foo!");
res.send(app.get('message'));
});
app.get('/bar', function(req, res) {
app.set('message', "Welcome to bar!");
// some long running async function
var foo = function() {
res.send(app.get('message'));
};
setTimeout(foo, 1000);
});
app.listen(3000);
If you visit /bar and another request hits /foo, your message will be "Welcome to foo!". This is a silly example, but it gets the point across.
There are some interesting points about this at Why do different node.js sessions share variables?.
const app = require('express')();
app.set('globalvar', "xyz");
app.get('globalvar');
I used app.all
The app.all() method is useful for mapping “global” logic for specific
path prefixes or arbitrary matches.
In my case, I'm using confit for configuration management,
app.all('*', function (req, res, next) {
confit(basedir).create(function (err, config) {
if (err) {
throw new Error('Failed to load configuration ', err);
}
app.set('config', config);
next();
});
});
In routes, you simply do req.app.get('config').get('cookie');
I solved the same problem, but I had to write more code.
I created a server.js file, that uses express to register routes.
It exposes a function,register , that can be used by other modules to register their own routes.
It also exposes a function, startServer , to start listening to a port
server.js
const express = require('express');
const app = express();
const register = (path,method,callback) => methodCalled(path, method, callback)
const methodCalled = (path, method, cb) => {
switch (method) {
case 'get':
app.get(path, (req, res) => cb(req, res))
break;
...
...
default:
console.log("there has been an error");
}
}
const startServer = (port) => app.listen(port, () => {console.log(`successfully started at ${port}`)})
module.exports = {
register,
startServer
}
In another module, use this file to create a route.
help.js
const app = require('../server');
const registerHelp = () => {
app.register('/help','get',(req, res) => {
res.send("This is the help section")
}),
app.register('/help','post',(req, res) => {
res.send("This is the help section")
})}
module.exports = {
registerHelp
}
In the main file, bootstrap both.
app.js
require('./server').startServer(7000)
require('./web/help').registerHelp()
John Gordon's answer was the first of dozens of half-explained / documented answers I tried, from many, many sites, that actually worked. Thank You Mr Gordon. Sorry I don't have the points to up-tick your answer.
I would like to add, for other newbies to node-route-file-splitting, that the use of the anonymous function for 'index' is what one will more often see, so using John's example for the main.js, the functionally-equivalent code one would normally find is:
app.get('/',(req, res) {
res.render('index', { title: 'Express' });
});

Resources