How to add a static route after a dynamic one in ExpressJS? - javascript

There are two 2 files with routes.
user.route.js
router.get('/user/:id');
user-transaction.js
router.get('/user/transaction');
My architecture looks like this:
src/
user/user.route.js
user-transaction/user-transaction.route.js
I connect all my routes in the project in the following way:
const dirs = await recursive(dir);
dirs.forEach((res) => {
if (res.includes('route')) {
console.log(res);
const path = `#modules/${res}}`;
router.use(require(path));
}
});
The problem is that Express doesn't see the /user/transaction route because it connects later than /user/:id and thinks /user/transaction refers to /user/:id.
It seems logical to just move the route higher, but I connect the routes dynamically. That is, it simply takes the files in order and connects the routes in the same order. Is there a solution to this problem or will I have to manually connect all the routes?

Related

Express.js - prepend sub-route to all defined routes

Let's say I have an Express app defined in a file, say server.js like this:
const app = express();
app.use('/foo', foo);
app.use('/bar', bar);
module.exports = app;
I import this Express app in another file, say index.js:
const app = require('./server');
const port = process.env.PORT || 3000;
const listen = (port) => {
app.listen(port, () => {
console.log(`Backend listening on port ${port}!`);
});
};
listen(port);
Now, the routes that are available for this app are /foo and /bar.
Is there a way to edit configuration in the index.js file so that the routes become /api/foo and /api/bar? Without touching server.js file.
Use case:
I have a Nuxt.js app with a backend that is loaded into the Nuxt app via serverMiddleware property in nuxt.config.js like this:
serverMiddleware: [
...
{ path: '/api', handler: '~/server.js' },
],
This has the effect similar to what I described above: it imports the express app from server.js app and prepends all its routes with /api.
However, often I don't want to develop the frontend part of the Nuxt app, I just want to do changes on the backend. For this purpose I have a helper file like index.js above, which runs backend only. (Frontend often takes long time to compile, that's why I don't want to compile it when I don't need to.)
This creates a problem that all the routes are slightly different - they lack the /api at the beginning. The routes are being used in different tools like Postman etc. and suddenly they wouldn't work.
My current solution is to define index.js file in the same way as server.js file with all routes defined like I want them - instead of app.use('/foo', foo); there's app.use('/api/foo', foo); etc. but this has its own problems, e.g. if I change server.js I have to change index.js. I am looking for something more elegant.
According to the express 4.0 docs https://expressjs.com/en/4x/api.html#app.use you can use an application instance the same as you would a router. In short, just use the export of your server.js as a middleware at the route in which you want to insert it, as opposed to directly calling .listen() on it.
Here is some demo code that worked for me:
const express = require('express');
const app_inner = express();
app_inner.use('/foo', (req,res) => res.send('foo'));
const app_outer = express();
app_outer.use('/foo2', app_inner);
app_outer.listen(9999);
// web browser at localhost:9999/foo2/foo returns 'foo' as expected

Express.js returning multiple routes in a single exported router object returns incorrect route

I am trying to utilize an MVC pattern for express. I am modularizing routes and trying to declare the express router in only the server entry file and nowhere else.
My current issue is when exporting my appRoute function in my main routes file (see below), the first route (user route), returns users. I have another route called game that is exported in the same function but it still returns users instead of games.
Both routes have a controller function called getAll that gets different data from different tables.
If I try and visit the route: http://localhost:8000/user/getAll, it returns all users just fine.
If I try and visit the route: http://localhost:8000/game/getAll, it still returns all users even when they're different routes...
If I were to flip the order of users and games in the main routes file where game is first and user is second, users starts to return games. It's like the second route mimics the first route.
This may be something simple, but any help I will appreciate.
My code is as shown below
server entry point (index.js)
const app = express();
const router = express.Router();
const bootstrap = require('./src/bootstrap');
bootstrap(app, router);
I am passing on the app and router instance to my bootstrap file where all routes will be exported to.
bootstrap.js (this file gets all exported routes and uses them within the app)
const { appRoute } = require('./routes');
module.exports = (app, router) => {
return app.use('/', appRoute(router));
};
I am passing on the router instance to my main routes file where all routes are exported from.
Main routes file (index.js): this file requires all routes and uses them within the router instance. I think this might be where my issue is but I am a little stuck on how I might fix it.
const { userRoute } = require('./userRoute');
const { gameRoute } = require('./gameRoute');
exports.appRoute = (router) => {
router.use('/user', userRoute(router));
router.use('/game', gameRoute(router));
return router;
};
Game route files (index.js): returns all users instead of games
const { gameController } = require('../../controllers');
exports.gameRoute = (router) => {
return router.get('/getAll', gameController.getAll);
};
Any help is greatly appreciated. If there is any clarification needed please let me know.
I think you need to create a separate router for game and user.
See the express.Routing section and birds.js example here

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 Express - separate my controllers and models

I'm building my first Express app. It's a bit messy as my controllers and my models are all in the same place: the app.js file.
Is there a way that I can separate those?
Even artificially, by creating different files and then using some third party program to compile them into the app.js file.
First of all, you need to create your controllers and model folders.
You can use a module called express-load which can be used to autoload models, routes, schemas, configs, controllers, object maps... etc...
in your main file, mine is called app.js you load them right before start the server code line.. it should look like
//requires....
var load = require('express-load');
//your code
load('models')
.then('controllers')
.then('routes')
.into(app);
http.createServer(app).listen(app.get('port'), function(){
console.log("Express listening on port "+ app.get('port'));
});
module.exports = app;
Then, your view folder you can create folders to keep your code organized, then subfolders, I created a folder called home, and inside of it my index view.
In my controllers folder I created a js file called home.js, and which will look for my index view:
module.exports = function(app){
var HomeController = {
index: function(req, res){
res.render('home/index');
}
};
return HomeController;
}
At last in your routes folder, you can set your application routes, each view needs to be specified in your controller. My file for routes its called home.js
module.exports = function(app){
var home = app.controllers.home;
app.get('/', home.index);
}
What I generally do it is to write a module which contains all the routes definition and load it in app.js e.g
require('./routes')(app);
My ./routes.js generally looks like this
module.exports = function (app) {
log.info('Loading express routes...');
/* registration */
app.post('/users', require('./routes/register-users')); // register user
app.post('/agents', require('./routes/register-agents')); // register agents
};
and I keep all the routes (.js) files inside a directory call routes
Hope it is what you are looking for.
Is there a way that I can separate those?
Yes, and you should separate them.
What most people do is declare the routes in the main app.js file and include separate files for the controllers (just like Rituparna described).
Those controllers files will in turn very likely include your model files via a require. For example.
In app.js
var blogRoutes = require('./routes/blogRoutes');
app.get('/api/blog/all', blogRoutes.all);
In routes\blogRoutes.js
var model = require('../models/blogModel');
var all = function(req, res) {
// your controller logic
// and very likely calls to your model
var m = model.blog(whatever);
res.send(m.something());
};
module.exports = {
all: all
}
In models\blogModel.js
var something = function() {
return "something";
};
module.exports = {
something: something
}
You can see a working version of this in this repo https://github.com/hectorcorrea/hectorcorrea.com
You should checkout the examples from the Express Github repo, there are multiple ways to do this (based on personal preference):
https://github.com/visionmedia/express/tree/master/examples/mvc
https://github.com/visionmedia/express/blob/master/examples/resource/app.js
https://github.com/visionmedia/express/tree/master/examples/route-separation
There are some examples here that may help you..
Route Separation: https://github.com/visionmedia/express/tree/master/examples/route-separation
MVP: https://github.com/visionmedia/express/tree/master/examples/mvc

How can I do my routing without having one long file of all the routes in nodejs?

Using this link as a reference https://github.com/visionmedia/express/tree/master/examples/route-separation to what "could be done"
I AM NOT USING EXPRESS. I AM USING THEM AS AN EXAMPLE.
I want to do something like this but "simpler" ...
How can I get away from declaring all my routes in one long, complex list all in one file? Can I define them by passing a router into my modules, and then including all the code in one directory ... ok, I'll suffer having one long document that only does "require" includes, like an index.js, for this one ~ at least that one my build scripts can rebuild for me, but preferably not in my primary file for every single route that I may add.
So for instance, they use this code:
// General
app.get('/', site.index);
// User
app.all('/users', user.list);
app.all('/user/:id/:op?', user.load);
app.get('/user/:id', user.view);
app.get('/user/:id/view', user.view);
app.get('/user/:id/edit', user.edit);
app.put('/user/:id/edit', user.update);
// Posts
app.get('/posts', post.list);
I want to avoid making a list like that in my app.js. I want instead to have each file know what the routes are for that file.
Here's what I'm wanting to do: (please don't critique the code, I'm making it very simple so I make sure that I am illustrating my code the way I want to do it)
//app.js
var router = require('./myRouter.js')
var includes = require('./routes/*.js').register(router)
// do some stuff here with an https server and start the server here
and
//./routes/user.js
var myRouter;
exports.register(router){
myRouter = router;
}
router.addRoute(/* here I do the magic associated with this route */)
Can I do it just that simply? What am I missing here?
I haven't written this code because I'm just ever so certain that I'm going about this the wrong way.
And if I am going to have to use something like an index.js in the /routes/ folder, can I use that same concept that I demonstrated I would like to use in my code of .register(router) appended so I can pass that information down recursively? Would that work?
I use an index.js file for this and use require("routes") which is a folder.
// app.js
route = require("./routes"),
...
route(app);
// routes/index.js
var index = require("./index-route"),
about = require("./about-route"),
posts = require("./posts-route");
module.exports = function(app) {
index(app);
about(app);
posts(app);
};
This works because if you require a folder it will load index.js by default.
If you have a lot of routes you might want to load them based on convention
var routes = [];
// read all files
fs.readdir("./", function(files) {
files.forEach(function(val) {
// require all non-index.js files.
if (val !== "index.js") {
routes.push(require(val));
}
});
});
module.exports = function(app) {
// for each route you required call it with app.
routes.forEach(val.bind(null, app));
}
This would load all .js files that are not "index.js", so any file in your /routes/ folder would be loaded and run when you route them.
Your solution looks vaguely like you wish to use the Visitor Patern, in which case I suggest you make ./roots/ require-able (see this question) and in index.js you include all the files you wish (as local's) and export a register module which calls the register module on each of the required files.
Or you could copy the code from the above answer directly into your main file.

Categories

Resources