Is there a better way to structure Express (nodejs) application routes? - javascript

I have my app.js where I have the server created and this is where I define the main routes. I only added parts of my code.
const app = express();
const user = require("./routes/user/user");
app.use("/user", user);
In the ./routes/user/user.js I am able to define the routes like this
const express = require("express");
const router = express.Router();
router.get("/profile", (req, res) => {
/*some more code*/
});
router.post("/register", (req, res) => {
/*some more code*/
});
router.get("/timeline", (req, res) => {
/*some more code*/
})
module.exports = router;
Right now ./routes/user/user.js is not messy, but when we add more routes it can get pretty ugly.
My goal would be to have ./routes/user/combineUserRoutes.js where I could combine multiple requests without defining them there.
These request files would be structured something like this:
./routes/user/auth/register.js
./routes/user/profile/profile.js
./routes/user/timeline/index.js

Related

How can I use my verifyToken middleware in express router modules?

I have an express app like so:
// index.js
const express = require('express');
const app = express();
const userRoutes = require('./routes/userRoutes');
app.use('/user', userRoutes);
const verifyToken = (req, res, next) => {
// validate req.cookies.token
next();
}
And I'm using an express router module like this:
// routes/userRoutes.js
const express = require('express');
const router = express.Router();
router.get('/:userid/data', verifyToken, async (req, res) => {
const data = await db.query()
res.json(data)
});
Obviously this doesn't work because verifyToken is not accesible within the module. How can I use the same verifyToken middleware function throughout different express modules?
Move verifyToken to a different file and export it from there.
Then you can import it in other places.
One thing that you can do, that works well is to group all your authed routes under a common path and use router.use to make sure that you apply the verifyToken middleware on all of them.

Is it possible a single API handles multiple requests in Node JS?

My goal is to create an API that handles multiple requests. By doing this, I need to pass a string as an argument to the url of API like this:
// index.js in client
fetch(`http://localhost:4000/routerName/${tableName}`).then()
// router.js
router.get(`/${tableName_from_client_page}`, (req, res) => { // Do Something })
A problem is, the browser can't connect to the targeted pages unless I create a whole new APIs for every matching tableNames.
I want my API handles multiple requests by receiving the tableName as its /url.
Are there some tricks to solve this problem?
This is how my whole router looks like:
// Router
const express = require('express'),
db = require('./db.js'),
router = express.Router();
router.get('/table', (req, res) => {
db.loadTable('SELECT * FROM someTable', res);
}) // Handles only one request on the name of url; /table
router.get(`/${tableName_from_client_page}`, (req, res) => {
db.loadTable(`SELECT * FROM ${tableName_from_client_page}`, res)
}) // Handles multiple requests, depending on its argument.
module.exports = router;
// Router
const express = require('express'),
db = require('./db.js'),
router = express.Router();
router.get('/table', (req, res) => {
db.loadTable('SELECT * FROM someTable', res);
}) // Handles only one request on the name of url; /table
router.get('/tables/:tableName', (req, res) => {
db.loadTable(`SELECT * FROM ${req.params.tableName}`, res)
}) // Handles multiple requests, depending on its argument.
module.exports = router;
// Router
const express = require('express'),
db = require('./db.js'),
router = express.Router();
This API will only handle one request "/table".
router.get('/table', (req, res) => {
db.loadTable('SELECT * FROM someTable', res);
})
To handle multiple requests checkout below code
but make sure to write this API last in the route file, If you write this API before the "/table" API then your "/table" request will also be handled by this API.
router.get('/:table_name', (req, res) => {
db.loadTable(`SELECT * FROM ${req.params.table_name}`, res)
})
module.exports = router;

Request parameter in Express router

I'm having some trouble accessing request parameters in express router.
My server.js file has this:
app.use('/user/:id/profile', require('./routes/profile');
And this is in my ./routes/profile.js file:
router.get('/', (req, res) => {
console.log(req.params.id);
}
But the console log prints undefined.
I'm new to express and feel like I'm missing something basic about how routing works.
Can someone please help me out?
Here is my full server.js:
const express = require('express');
const app = express();
app.use(express.json());
app.use('/user/:id/profile', require('./routes/profile'));
app.listen(5000, () => console.log('Listening'));
Here is my full profile.js:
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
console.log(req.params.id);
res.status(200).send('In profile route');
});
module.exports = router;
URL parameters are not exposed to routers. You have a couple of options here:
Parse req.originalUrl to get the user id (not recommended). req.originalUrl isn't affected by the router's mount point and will remain as /user/112/profile or whatever url you visited.
Add some middleware to expose the id (recommended). Your new route statement will end up looking like this:
(Now you can use req.userId in your profile.js)
app.use('/user/:id/profile', function(req, res, next) {
req.userId = req.params.id;
next();
}, require('./routes/profile'));
Change the mount point from /user/:id/profile to /user, then edit your router to listen on /:id/profile (not recommended).

Structure event listeners in Node.js [duplicate]

This question already has answers here:
How to separate routes on Node.js and Express 4?
(9 answers)
Closed 1 year ago.
In my NodeJS express application I have app.js that has a few common routes. Then in a wf.js file I would like to define a few more routes.
How can I get app.js to recognize other route handlers defined in wf.js file?
A simple require does not seem to work.
If you want to put the routes in a separate file, for example routes.js, you can create the routes.js file in this way:
module.exports = function(app){
app.get('/login', function(req, res){
res.render('login', {
title: 'Express Login'
});
});
//other routes..
}
And then you can require it from app.js passing the app object in this way:
require('./routes')(app);
Have a look at these examples: https://github.com/visionmedia/express/tree/master/examples/route-separation
In Express 4.x you can get an instance of the router object and import another file that contains more routes. You can even do this recursively so your routes import other routes allowing you to create easy-to-maintain URL paths.
For example, if I have a separate route file for my /tests endpoint already and want to add a new set of routes for /tests/automated I may want to break these /automated routes out into a another file to keep my /test file small and easy to manage. It also lets you logically group routes together by URL path which can be really convenient.
Contents of ./app.js:
var express = require('express'),
app = express();
var testRoutes = require('./routes/tests');
// Import my test routes into the path '/test'
app.use('/tests', testRoutes);
Contents of ./routes/tests.js:
var express = require('express'),
router = express.Router();
var automatedRoutes = require('./testRoutes/automated');
router
// Add a binding to handle '/tests'
.get('/', function(){
// render the /tests view
})
// Import my automated routes into the path '/tests/automated'
// This works because we're already within the '/tests' route
// so we're simply appending more routes to the '/tests' endpoint
.use('/automated', automatedRoutes);
module.exports = router;
Contents of ./routes/testRoutes/automated.js:
var express = require('express'),
router = express.Router();
router
// Add a binding for '/tests/automated/'
.get('/', function(){
// render the /tests/automated view
})
module.exports = router;
Building on #ShadowCloud 's example I was able to dynamically include all routes in a sub directory.
routes/index.js
var fs = require('fs');
module.exports = function(app){
fs.readdirSync(__dirname).forEach(function(file) {
if (file == "index.js") return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
}
Then placing route files in the routes directory like so:
routes/test1.js
module.exports = function(app){
app.get('/test1/', function(req, res){
//...
});
//other routes..
}
Repeating that for as many times as I needed and then finally in app.js placing
require('./routes')(app);
If you're using express-4.x with TypeScript and ES6, this would be the best template to use:
src/api/login.ts
import express, { Router, Request, Response } from "express";
const router: Router = express.Router();
// POST /user/signin
router.post('/signin', async (req: Request, res: Response) => {
try {
res.send('OK');
} catch (e) {
res.status(500).send(e.toString());
}
});
export default router;
src/app.ts
import express, { Request, Response } from "express";
import compression from "compression"; // compresses requests
import expressValidator from "express-validator";
import bodyParser from "body-parser";
import login from './api/login';
const app = express();
app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(expressValidator());
app.get('/public/hc', (req: Request, res: Response) => {
res.send('OK');
});
app.use('/user', login);
app.listen(8080, () => {
console.log("Press CTRL-C to stop\n");
});
Much cleaner than using var and module.exports.
Full recursive routing of all .js files inside /routes folder, put this in app.js.
// Initialize ALL routes including subfolders
var fs = require('fs');
var path = require('path');
function recursiveRoutes(folderName) {
fs.readdirSync(folderName).forEach(function(file) {
var fullName = path.join(folderName, file);
var stat = fs.lstatSync(fullName);
if (stat.isDirectory()) {
recursiveRoutes(fullName);
} else if (file.toLowerCase().indexOf('.js')) {
require('./' + fullName)(app);
console.log("require('" + fullName + "')");
}
});
}
recursiveRoutes('routes'); // Initialize it
in /routes you put whatevername.js and initialize your routes like this:
module.exports = function(app) {
app.get('/', function(req, res) {
res.render('index', { title: 'index' });
});
app.get('/contactus', function(req, res) {
res.render('contactus', { title: 'contactus' });
});
}
And build yet more on the previous answer, this version of routes/index.js will ignore any files not ending in .js (and itself)
var fs = require('fs');
module.exports = function(app) {
fs.readdirSync(__dirname).forEach(function(file) {
if (file === "index.js" || file.substr(file.lastIndexOf('.') + 1) !== 'js')
return;
var name = file.substr(0, file.indexOf('.'));
require('./' + name)(app);
});
}
I am trying to update this answer with "express": "^4.16.3". This answer is similar to the one from ShortRound1911.
server.js:
const express = require('express');
const mongoose = require('mongoose');
const bodyParser = require('body-parser');
const db = require('./src/config/db');
const routes = require('./src/routes');
const port = 3001;
const app = new express();
//...use body-parser
app.use(bodyParser.urlencoded({ extended: true }));
//...fire connection
mongoose.connect(db.url, (err, database) => {
if (err) return console.log(err);
//...fire the routes
app.use('/', routes);
app.listen(port, () => {
console.log('we are live on ' + port);
});
});
/src/routes/index.js:
const express = require('express');
const app = express();
const siswaRoute = require('./siswa_route');
app.get('/', (req, res) => {
res.json({item: 'Welcome ini separated page...'});
})
.use('/siswa', siswaRoute);
module.exports = app;
/src/routes/siswa_route.js:
const express = require('express');
const app = express();
app.get('/', (req, res) => {
res.json({item: 'Siswa page...'});
});
module.exports = app;
If you want a separate .js file to better organize your routes, just create a variable in the app.js file pointing to its location in the filesystem:
var wf = require(./routes/wf);
then,
app.get('/wf', wf.foo );
where .foo is some function declared in your wf.js file. e.g
// wf.js file
exports.foo = function(req,res){
console.log(` request object is ${req}, response object is ${res} `);
}
One tweak to all of these answers:
var routes = fs.readdirSync('routes')
.filter(function(v){
return (/.js$/).test(v);
});
Just use a regex to filter via testing each file in the array. It is not recursive, but it will filter out folders that don't end in .js
I know this is an old question, but I was trying to figure out something like for myself and this is the place I ended up on, so I wanted to put my solution to a similar problem in case someone else has the same issues I'm having. There's a nice node module out there called consign that does a lot of the file system stuff that is seen here for you (ie - no readdirSync stuff). For example:
I have a restful API application I'm trying to build and I want to put all of the requests that go to '/api/*' to be authenticated and I want to store all of my routes that go in api into their own directory (let's just call it 'api'). In the main part of the app:
app.use('/api', [authenticationMiddlewareFunction], require('./routes/api'));
Inside of the routes directory, I have a directory called "api" and a file called api.js. In api.js, I simply have:
var express = require('express');
var router = express.Router();
var consign = require('consign');
// get all routes inside the api directory and attach them to the api router
// all of these routes should be behind authorization
consign({cwd: 'routes'})
.include('api')
.into(router);
module.exports = router;
Everything worked as expected. Hope this helps someone.
index.js
const express = require("express");
const app = express();
const http = require('http');
const server = http.createServer(app).listen(3000);
const router = (global.router = (express.Router()));
app.use('/books', require('./routes/books'))
app.use('/users', require('./routes/users'))
app.use(router);
routes/users.js
const router = global.router
router.get('/', (req, res) => {
res.jsonp({name: 'John Smith'})
}
module.exports = router
routes/books.js
const router = global.router
router.get('/', (req, res) => {
res.jsonp({name: 'Dreams from My Father by Barack Obama'})
}
module.exports = router
if you have your server running local (http://localhost:3000) then
// Users
curl --request GET 'localhost:3000/users' => {name: 'John Smith'}
// Books
curl --request GET 'localhost:3000/books' => {name: 'Dreams from My Father by Barack Obama'}
I wrote a small plugin for doing this! got sick of writing the same code over and over.
https://www.npmjs.com/package/js-file-req
Hope it helps.
you can put all route functions in other files(modules) , and link it to the main server file.
in the main express file, add a function that will link the module to the server:
function link_routes(app, route_collection){
route_collection['get'].forEach(route => app.get(route.path, route.func));
route_collection['post'].forEach(route => app.post(route.path, route.func));
route_collection['delete'].forEach(route => app.delete(route.path, route.func));
route_collection['put'].forEach(route => app.put(route.path, route.func));
}
and call that function for each route model:
link_routes(app, require('./login.js'))
in the module files(for example - login.js file), define the functions as usual:
const login_screen = (req, res) => {
res.sendFile(`${__dirname}/pages/login.html`);
};
const forgot_password = (req, res) => {
console.log('we will reset the password here')
}
and export it with the request method as a key and the value is an array of objects, each with path and function keys.
module.exports = {
get: [{path:'/',func:login_screen}, {...} ],
post: [{path:'/login:forgotPassword', func:forgot_password}]
};

Dynamic routes with different functions in express js

I have loads of router.get functions in my code which I think, could be reduced to a single switch-case function. Here is what I have tried:
function handlerA(req, res) {}
function handlerB(req, res) {}
var routes = {
'/url-one': handlerA,
'/url-two': handlerB
}
router.get('/*', function(req, res) {
var url = req.url;
if (routes[url]) {
routes[url](req, res);
}
});
This works but also, significantly slows my application. Is there any other solution which would not hit the performance of my app?
Thanks
Is there a reason you don't want to use router.get functions? I would guess express.js is internally performing the same logic that you are doing anyway. You are just replacing get functions with handlers.
If you are using similar logic between multiple routes, that may be worth abstracting.
I usually go with a setup like this:
app.js
routes.js
api/
user/
index.js
user.controller.js
user.model.js
image/
index.js
image.controller.js
image.model.js
/api/user/index.js:
var express = require('express');
var controller = require('./user.controller');
var router = express.Router();
router.get('/', controller.index);
router.post('/', controller.create);
module.exports = router;
/api/user/user.controller.js:
var User = require('./user.model');
exports.index = function(req, res) {
// Show list of users
};
exports.create = function (req, res, next) {
// Create user
};
/routes.js:
module.exports = function(app) {
// Insert routes below
app.use('/api/users', require('./api/user'));
app.use('/api/images', require('./api/image'));
// All undefined asset or api routes should return a 404
app.route('/:url(api|auth|components|app|bower_components|assets)/*')
.get(errors[404]);
// All other routes should redirect to the index.html
app.route('/*')
.get(function(req, res) {
res.sendfile(app.get('appPath') + '/index.html');
});
};
And lastly, the /app.js:
// Set default node environment to development
process.env.NODE_ENV = process.env.NODE_ENV || 'development';
var express = require('express');
var mongoose = require('mongoose');
var config = require('./config/environment');
// Connect to database
mongoose.connect(config.mongo.uri, config.mongo.options);
// Populate DB with sample data
if(config.seedDB) { require('./config/seed'); }
// Setup server
var app = express();
var server = require('http').createServer(app);
require('./config/express')(app);
require('./routes')(app);
// Start server
server.listen(config.port, config.ip, function () {
console.log('Express server listening on %d, in %s mode', config.port, app.get('env'));
});
// Expose app
exports = module.exports = app;
Most of this is directly from the Yeoman Generator Angular-Fullstack and it has a really nice setup!

Categories

Resources