Dynamically Adding Routes - javascript

I am trying to create a plugin folder that will have various plugins. So, for example if there is a plugin called "admin", there would be a /plugins/admin folder which would have a adminOnly file that looks something like this:
router.get('/', (req, res, next) => {...})
router.get('/link1', (req, res, next) => {...})
router.post('/link2', (req, res, next) => {...})
router.get('/link3', (req, res, next) => {...})
.
.
.
Previously, in my app.js file I have something like:
const adminOnly = require('.../plugins/admin/adminOnly'
...
app.use('/adminOnly', adminOnly)
This results in if i go to www.website.com/adminOnly/link1 I get whatever is in the router.get for link 1.
However, I want to make it all dynamic so that I can change my app.js logic so that it is something like this:
app.use((req, res, next) => {
const possiblePlugin = require('.../plugins/${req.baseUrl}')
res.render<or something> (possiblePath)
}
The idea is to attempt to go to a path that the front end requests and see what is there but to dynamically add the paths at runtime so anyone can add plugins whenever and they work.

Create a folder for every route. So your file system would be like this:
/plugins
-><name of plugin>/
---->index.js
---->otherFilesNeededForThisPlugin.js
Secondly, instead of searching for the route once someone requests it, it makes more sense to add all the routes when you start the server. So, you can read all the folders in /plugins and then for each one add there index.js file. Something like this:
// adds all plugins. Plugins should be in the 'plugins' folder
// and the route should be named 'index.js'
fs.readdirSync(join(__dirname, 'plugins')).forEach((fileOrFolder) => {
const fullFilePath = join(__dirname, 'plugins', fileOrFolder);
if (fs.lstatSync(fullFilePath).isDirectory() && fs.existsSync(`${fullFilePath}/index.js`)) {
const pluginName = `/${fileOrFolder}`;
const pluginModule = require(fullFilePath); // eslint-disable-line
app.use(pluginName, pluginModule);
}
});

Related

How to use express.Router instance for error handling

According to the documentation, any nodejs express middleware function can be replaced by App or Router instances:
Since router and app implement the middleware interface, you can use
them as you would any other middleware function.
This is some generic error handling I use:
express()
.use('/test', new TestRouter())
.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send(err.message);
})
.listen(PORT);
I tried to replace my error handling with an error-handling Router, but now the callback is never executed and express uses it's default error handling.
const handler = new express.Router();
handler.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send(err.message);
});
express()
.use('/test', new TestRouter())
.use(handler)
.listen(PORT);
Why is this not working as expected and how could I solve it?
Error handlers need to be configured as the last calls to use, as per the docs
You define error-handling middleware last, after other app.use() and routes calls;
I would say "routes" also buckets Routers therefore what you are trying to do doesn't look like it's supported.
Just digging further into this, I believe the issue is down to the fact Routers have separate Layer stacks. Express behind the scenes is effectively just a Router (we see here where this is setup, and then further down where the middleware is delegated on to the Router). Internally, middleware functions are represented as "Layers" (as seen here) and looking at the constructor, we can see Routers have their own stack.
So consider the following example:
express()
.use(new RouterA())
.use(new RouterB())
.use((err, req, res, next) => {
...
})
.listen(PORT);
Which, by looking at the source, can be viewed as:
express()
.use((req, res, next) => {
router.handle(req, res, next);
})
.use((req, res, next) => {
router.handle(req, res, next);
})
.use((err, req, res, next) => {
...
});
Therefore, if an error is thrown in RouterA, the Router would firstly check its own middleware stack for a matching error Layer (i.e. a (err, req, res, next) function) and execute that, it would then bubble up to the app level and perform the same action.
So given your example, if you consider the translated code, this explains why it won't catch your error handler in the second Router - a Router signature does not match that of an error handler therefore it would be skipped.

Page renders using router.use but gives a 'Cannot GET. error with router.get

I am new to javascript and node.js I am learning online and trying to create a cms using node.js, express and sqlite. Before going to details I will give you guys my directory structure.
rootdir
node_modules
public
css (generic styling files)
vendor (bootstrap and js files)
routes
defaultRoutes.js
views
default
index.handlebars
partials
default
(header and footer files)
layouts
main.handlebars
app.js
package-lock.json
package.json
app.js is where the server is created and defaultRoutes are for routes. When I run
//code in defaultRoutes.js
const express = require('express');
const router = express.Router();
router.use((req,res) => {
res.render('default/index');
});
module.exports = router;
and
//code in app.js
//Routes
const defaultRoutes = require('./routes/defaultRoutes');
app.use(defaultRoutes);
the page renders but when I change the router.use in defaultRoutes to router.get the browser throws cannot GET error.
Can anyone explain why this is happening?
You need to define a route when using router.get(). router.use() can work for any route, but to define a route with router.get() you can add the route like this:
//code in defaultRoutes.js
const express = require('express');
const router = express.Router();
router.get('/', (req,res) => {
res.render('default/index');
});
module.exports = router;

How to put all app.use in a separate common file

I have multiple app.use in my index/starting point of my app.
i.e
app.use(
if (!req.contextToken && req.contextTokenchecked) {
req.queryToFirebase = false;
req.contextTokenchecked = true;
req.contextToken = {}
}
next()
)
app.use(//Do something 2)
and so on..
Now, This is sort of makes my code untidy (in index.js) so I thought about creating a separate js file (say intialize.js) which will contain all my app.use
Till now, I am used to only creating separate routes
const express = require('express')
const router = express.Router()
and then import it in my index.js
app.use('/auth', auth)
But this time I don't want my routes in separate file rather all
app.use()
In one common.js file
Second, I also have a route which loads data from gmail (gmail.js).
app.use('/gmail', gmail)
currently, In all the routes, I am adding a middleware isLoggedInmanually. Is it possible to do something so that all the routes inside it my gmail.js inherits that middleware
The middlewares you register are always executed in the order they are registered. So if you have a code like this:
app.use((req, res, next) => {
// middleware A
next()
})
app.use((req, res, next) => {
// middleware B
next()
})
app.use(middlewareC)
app.use('/gmail', gmail)
Then you can for sure create one common file for those middlewares before the app.use('/gmail', gmail):
common.js
let router = express.Router()
router.use((req, res, next) => {
// middleware A
next()
})
router.use((req, res, next) => {
// middleware B
next()
})
router.use(middlewareC)
module.exports = router
main.js
app.use(require('./common.js'))
app.use('/gmail', gmail)
The API for use (or any others of those registering methods) is ([path,] callback [, callback...])
So you can register as many middlewares as callback as you want, so you can add a isLoggedIn in front of the gmail router:
app.use('/gmail', isLoggedIn, gmail)

Subroutes in Next Js

I´m new to next js, I have created a file called orders.js under pages directory, and I can access it correctly from localhost:3000/orders.
However, I want now to have a subroute, to access the order with id 1 (for example). So I have created a directory 'orders' inside the directory pages, and renamed order.js to index.js, after that, I have created another file inside the orders directory called id.js.
So my current structure is:
pages/
orders/
index.js
id.js
However I cannot access to localhost:3000/orders/1.
Using Nuxt js, this was trivial, how can I achieve the same with next.js ?
Thanks
This is also trivial with Nextjs, however, you're trying to achieve it the harder way.
Your first approach is correct. If you don't specify a route for your pages in the server.js file, Nextjs will automatically use them if the URL is correct (in this case orders leads to the orders.js page).
What you're looking for is to create a custom route. You can see the documentation for this here
I find the example in the documentation confusing, so I recommend using express instead. Here's an example for that. You can then see the express routes in the server.js file of the example.
Your route would end up looking something like this:
server.get('/orders/:id', (req, res) => {
return app.render(req, res, '/orders', req.query)
})
Where :id is a query param which you can then access in your getInitialProps inside your orders.js page.
You can check the express routing examples in the express documentation.
You can try using next-routes, dynamic routes for Next.js
And simply create a routes.js and add,
const routes = require('next-routes')
module.exports = routes()
.add('orders', '/orders/:id', 'orders/id')
// name, url, page folder
Or if you only want the server side routing,
server.get('/orders/:id', (req, res) => {
const actualPage = '/orders'
app.render(req, res, actualPage, req.query)
})
This might help you : https://nextjs.org/docs#dynamic-routing.
by adding [ ] to a page it creates a dynamic route, in this case [orderid].js can be used to map multiple orders to a single page.
pages/
orders/
[id].js
use
pages/
orders/
[dynamic_subroute].js
now catch it with
const router = useRoute();
const { dynamic_subroute } = router.query;
Now, you can catch the value (any) dynamically from the url which is used instead of dynamic_subroute
like- if the url is pages/orders/1
then value of dynamic_subroute will be 1 in your page

NodeJs and ExpressJs middleware that relies on another middleware being used

I am creating an express middleware. My index.js exports is a function that returns a function(req,res,next) {...}
function myMiddleware (options) {
//some stuff with options...
return function middleware(req, res, next) {
//stuff
}
}
module.exports = myMiddleware;
So the user would could start using my middleware like this:
var express = require('express'),
myMiddleware = require('my-middleware'),
app = express();
app.use(myMiddleware());
The important thing is that I must make sure that middleware has access to cookies. Of course I could parse req.header.cookie myself but there exists a cookie-parser module that does the parsing for me.
So I would like to make sure that cookie-parser middleware is already being used when my middleware starts up. I could probably instruct the user to first use cookieParser() but I dislike it.
Is there a way to do it easily?
EDIT
I could even port cookie-parser since it is not a huge module. But that is probably the least thing I would do since it can introduce a lot of maintenance work that I would normally not do.
You have two options:
Force the cookie-parser middleware if req.cookies is undefined.
If cookie-parser has already been called, this call will be ignored. See: https://github.com/expressjs/cookie-parser/blob/master/index.js
The downfall to this approach is that the cookieParser middleware will only have the default options.
var cookieParser = require('cookie-parser');
function myMiddleware(options) {
return function(req, res, next) {
return cookieParser()(req, res, function() {
// Your middleware code
return next();
});
}
}
Give a warning if req.cookies is undefined
function myMiddleware(options) {
return function(req, res, next) {
if(!req.cookies) {
// Some warning
}
// Your middleware code
return next();
}
}
In express, the execution order of middleware is defined by the order it has been attached to the app/router.
req.param being an exception, as it is executed before any route handler that matches the parameter.
The order in which your routes and middleware functions are declared is the order which they will be executed. I do not know of a way to change this, maybe fiddling with the internals of express router, but there is no documented/clean way to achive this by default.
To make sure your middleware has access to the cookie parsed by cookie-parser, attach the cookie parsing middleware before any route definitions.
app.use(cookieParser());
// all other middleware that uses cookies

Categories

Resources