Splitting express app.get in multiple files - node.js

Is there any way to split it without needing to state each app.get?
Like this, I have to add a new app.get for each route.
app.get('/', require('./modules/menu'))
The goal is to make it automatic, so it'll get the path, parameters and stuff and send to the correct file.
Thanks!

Put all URL handler functions to separate files, for example, put all those handlers to files in directory ./modules.
Example handler file:
function handle(req,res){
res.send("file1");
}
module.exports = handle;
Use a for loop to create routes for app:
const glob = require("glob");
const glob_promise = require("glob-promise");
var log = console.log;
var files = [];
async function main(){
files = await glob_promise("./modules/**/*.js");
log(files);
for (let i=0; i<files.length; i++){
let file = files[i];
let url_path = file.replace("./modules","").replace(".js","");
log(url_path);
app.get(url_path, require(file))
}
}
main();

This is the way I work with express
app/
routes/
controllers
app.js
you can use app.js to import the files for controllers and routes
const routes1 = require("./routes/route1.json")
const routes2 = require("./routes/route2.json")
inside routes/route1.json you can then import your controllers for those routes:
let express = require('express')
const authTokenController = require("../controllers/auth/token")
let api = express.Router()
api.post('/my-controller', myController.myMethod)
module.exports = api
The good part is that you can use objects as controllers
class myController {
myMethod(request, response) {
}
}
module.exports = new authTokenController
So now you can load your very well organized app into express
app.use('/api', routes1)
app.use('/app', routes2)

Related

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);

Arangodb/Foxx how to split routes in different files?

How could I split the routing into different files?
This what I tried, but did not work:
// file 'index.js' as main in manifest.json
const createRouter = require('#arangodb/foxx/router');
const router = createRouter();
const entries = require('./routes/entries')
entries.init(router);
module.context.use("", router);
and the entries file is working as a function:
// file './routes/entries.js'
const db = require('#arangodb').db;
// [...] more const
module.exports = {
init: function(router) {
router.post('/entries', function(req, res) {
// [...] post handle
}
}
}
1) How could I modify the router in a js file and reuse in?
module.context.use(router)
2) Any idea how to handle all js files in the folder 'routes' to define the router and so minimize the definition for route files?
you can use the funtion router.use([path], middleware, [name]): Endpoint for that.
module.context.use('/entries', require('./routes/entries'), 'entries');
For more information take a look into the docs here or in the newest Foxx tutorial here which also use child routers.

How to keep app.js file small

I am working on node js. Day by day my app.js file is being longer. How can I reduce it. So that I can write code in other files. Still I have tried nothing but read about modules on google.
A simple example might go as follows, in your app.js file you set up your server:
const express = require('express');
const http = require('http');
const app = express();
const router = require('./router'); // <= get your router here
// Call the router, now you are listening
// using the routes you have set up in the other file
router(app);
const server = http.createServer(app);
server.listen(port, () => {
console.log('Server listening on port: ', port);
});
And in your router you export the app function using module.exports
module.exports = app => {
app.get('/', function(req, res) {
res.end('hello');
}
// all your routes here
}
Now you have separated out the logic of routing.
You can also export multiple methods or variables with the same process.
myFuncs.js
func1 function() {}
func2 function() {}
module.exports = {
func1
func2
}
(note here I am using ES6 it is the same as module.exports = { func1: func1, func2: func2 }
and then require them in the same way
const myFuncs = require('./myFuncs')
myFuncs.func1() // <= call func1
myFuncs.func2() // <= call func2
You can do the same operation with variables, and even combine with module.exports to shorten your code
mySecrets.js
module.exports = {secret_key: 'verysecretkey'}
app.js
const secret = require('./mySecrets')
That way you can keep your api_keys etc.. in a separate file, or even just variables that you want to import as required.
There are more details available here: https://developer.mozilla.org/en/docs/web/javascript/reference/statements/export
You can separate the code into different files and then use require to import them to app.js
var init = require('./config/init')(),
config = require('./config/config')
In the above code I have separated out some init function and config files to a separate files and I am importing it.
Write a module within an external file e.g.: ./hello.js:
module.exports = {
function1 : function(){console.log('hello module');}
};
Load that module within your app.js:
var hello = require('./hello.js');
// call your function
hello.function1();

How to modularize routing with Node.js Express

I'm building a web app with Express and Node and am trying to factor my routing so that I don't have hundreds of routes in the same file. This site serves different files within the projects directory, so I made a file in routes/ called projectRoutes.jsto handle the routing for project files:
var express = require('express');
module.exports = function() {
var functions = {}
functions.routeProject = function(req, res) {
res.render('pages/projects/' + req.params.string, function(err, html) {
if (err) {
res.send("Sorry! Page not found!");
} else {
res.send(html);
}
});
};
return functions;
}
Then, in my routes.js, I have this...
var projectRoutes = require("./projectRoutes");
router.get('/projects/:string', function(req, res) {
projectRoutes().routeProject(req, res);
});
Is there a better way to structure this functionality within projectRoutes.js? In other words, how can I configure projectRoutes.js so that I can write the follow line of code in index.js:
router.get('/projects/:string', projectRoutes.routeProject);
The above seems like the normal way to handle something like this, but currently the above line throws an error in Node that says the function is undefined.
Thanks for your help!
You should use the native express router, it was made to solve this exact problem! It essentially lets you create simplified nested routes in a modular way.
For each of your resources, you should separate out your routes into several modules named <yourResource>.js. Those modules would contain all of the routing code as well as any other configuration or necessary functions. Then you would attach them in index.js with:
var apiRoute = router.route('/api')
apiRoute.use('/< yourResource >', yourResourceRouter)
For example, if you had a resource bikes:
In index.js:
var apiRoute = router.route('/api')
, bikeRoutes = require('./bikes')
apiRoute.use('/bikes', bikeRoutes)
Then in bike.js:
var express = require('express')
, router = express.Router()
, bikeRoutes = router.route('/')
bikeRoutes.get(function (req, res) {
res.send('api GET request received')
});
module.exports = bikeRoutes
From there its easy to see that you can build many different resources and continually nest them.
A larger of example of connecting the routes in index.js would be:
var apiRoute = router.route('/api')
, bikeRoutes = require('./bikes')
, carRoutes = require('./cars')
, skateboardRoutes = require('./skateboards')
, rollerskateRoutes = require('./rollerskates')
// routes
apiRoute.use('/bikes', bikeRoutes)
apiRoute.use('/cars', carRoutes)
apiRoute.use('/skateboards', skateboardRoutes)
apiRoute.use('/rollerskates', rollerskateRoutes)
Each router would contain code similar to bikes.js. With this example its easy to see using express's router modularizes and makes your code base more manageable.
Another option is to use the Router object itself, instead of the Route object.
In Index.js:
//Load Routes
BikeRoutes = require('./routes/Bike.js');
CarRoutes = require('./routes/Car.js');
//Routers
var express = require('express');
var ApiRouter = express.Router();
var BikeRouter = express.Router();
var CarRouter = express.Router();
//Express App
var app = express();
//App Routes
ApiRouter.get('/Api', function(req, res){...});
ApiRouter.use('/', BikeRouter);
ApiRouter.use('/', CarRouter);
In Bike.js:
var express = require('express');
var router = express.Router();
router.get('/Bikes', function(req, res){...});
module.exports = router;
Similarly in Car.js

Dynamically load routes with express.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

Resources