Introduction
So far I have three files, one test.js is a file where I have built three functions that work.
But now I am trying to structure using MVC or at least some pattern. So now I router.js and app.js
Question
Should I put my promise functions from test.js in my config.js or server.js or something else, Im just interested in how people would do this and whats the correct way of structuring NodeJS.
server.js
In here start the server and apply the routes to my app
var configure = require('./router');
var express = require('express');
var app = express();
var port = process.env.PORT || 8080;
// get an instance of router
var router = express.Router();
configure(router);
app.listen(port);
console.log('Server has started!! ' + port);
// apply the routes to our application
app.use('/', router);
config.js
In here I build my routes
module.exports = function (router) {
// route middleware that will happen on every request
router.use(function (req, res, next) {
// log each request to the console
console.log(req.method, req.url);
// continue doing what we were doing and go to the route
next();
});
// home page route (http://localhost:8080)
router.get('/', function (req, res) {
res.send('im the home page!');
});
// sample route with a route the way we're used to seeing it
router.get('/sample', function (req, res) {
res.send('this is a sample!');
});
// about page route (http://localhost:8080/about)
router.get('/about', function (req, res) {
res.send('im the about page!');
});
// route middleware to validate :name
router.param('name', function (req, res, next, name) {
// do validation on name here
console.log('doing name validations on ' + name);
// once validation is done save the new item in the req
req.name = name;
// go to the next thing
next();
});
// route with parameters (http://localhost:8080/hello/:name)
router.get('/hello/:name', function (req, res) {
res.send('hello ' + req.params.name + '!');
})
// app.route('/login')
// show the form (GET http://localhost:8080/login)
.get('/login', function (req, res) {
res.send('this is the login form');
})
// process the form (POST http://localhost:8080/login)
.post('/login', function (req, res) {
console.log('processing'); // shows on console when post is made
res.send('processing the login form!'); // output on postman
});
};
test.js
In here is a list of functions that are a chain of promises getting data and API Keys
(small function, one of many that feed into each over)
var firstFunction = function () {
return new Promise (function (resolve) {
setTimeout(function () {
app.post('/back-end/test', function (req, res) {
console.log(req.body);
var login = req.body.LoginEmail;
res.send(login);
resolve({
data_login_email: login
});
});
console.error("First done");
}, 2000);
});
};
My recommended structure is to put everything except server.js in lib directory so all your app is lib/ plus server.js - everything else is package.json, dependencies in node_modules (created on npm install, not in the repo), .gitignore, config files for Travis, Circle, Heroku or whatever service you're using, some README.md and things like that.
Now, server.js is just bare minimum that requires lib/app:
const app = require('./lib/app');
and starts the server with something like:
const server = app.listen(app.get('port'), () => {
logger.info('%s listening on port %s', app.get('name'), app.get('port'));
});
server.on('error', (err) => {
logger.error(err.message || err);
process.exit(1);
});
where logger is some higher lever logger like Winston or something like that.
That's it. Now, lib/app.js is minimum code that loads the middleware like body parsers etc., creates the express app and sets the variables for port and name and then uses a router that is exported by lib/routes:
const routes = require('./routes');
// ...
app.use('/', routes);
The lib/app should be enough to use for testing with tools like supertest but it doesn't listen on any port - server.js does. This is important to simplify testing.
The router exported by lib/routes is used for everything and you can start with a single lib/routes.js file and then convert it to lib/routes/index.js plus several files in lib/routes as needed.
The routes only define the actual routes and input validation with a module like e.g. express-validation and register controllers that are exported by lib/controllers - that can start as lib/controllers.js and get converted to lib/controllers/index.js plus lib/controllers/*.js as needed - just like the routes.
Then I would add top level spec or test or tests directory where all of the tests go. The tests can require your lib/app to run the tests on it with no need to listen on actual TCP ports - those will test your routes with actual controllers. Other tests will require lib/util and run some unit tests on your utilities etc. Make sure to use a tool like istanbul or nyc to calculate the test coverage.
The database schemas and data models would go to lib/schemas and lib/models, some utility helpers in lib/util, some config loading code in lib/config etc.
This is quite flexible layout and works pretty well. You can start with just few files:
README.md
LICENSE.md
package.json
server.js
lib/app.js
lib/routes.js
lib/controllers.js
lib/config.js
etc. and easily convert all of the xxx.js file into xxx/index.js with entire folder of smaller xxx/*.js files as needed.
The main difference from your approach is that I recommend exporting routers and using them by higher level routers instead of passing the high level router into lower lever modules that export functions that take routers to work on.
So instead of:
const moreSpecific = require('more-specific');
const moreGeneral = express.Router();
moreSpecific(moreGeneral);
and then in more specific:
module exports = (router) => {
router.use('/abc/xyz', ...);
};
I would recommend exporting a more specific router in a file e.g. routes/abc.js:
const router = express.Router();
router.use('/xyz', ...);
module exports = router;
and then in more general router e.g. in routes/index.js:
const abc = require('abc');
const router = express.Router();
router.use('/abc', abc);
// and export the main router for other modules like app.js to use:
module.exports = router;
to have a route like /abc/xyz.
Related
I made a Express.js system where the files in the /routes folder are acting as a classic route (but with one file per route)
Example: /routes/get/user.js will be accessible with http://localhost:8080/user (the /get is to separate methods, it can be /post, /put...)
Here's my entire index.js file: https://pastebin.com/ALtSeHXc
But actually, my problem is that I can't pass params into the url like https://localhost:8080/user/random_id_here.
With this system, I think the best idea is to find a way to pass params on separated files too, but I don't know how can it be done...
Here's an example of one of my separated file:
module.exports = class NameAPI {
constructor(client) {
this.client = client
}
async run(req, res) {
// Code here
}
}
Maybe you'll have a better system, or a solution for this. Thanks.
You can get the optional params from the module object you already have so each module specifies its own params. This example below shows just adding new params on after the module name, but you could extend this feature to be richer if you needed to.
In a simple implementation, in your loader, you can change this:
posts.forEach((post) => {
const module = new (require(`./routes/post/${post}`))(this);
this.api.post(`/${post}`, async (req, res) => await module.run(req, res))
})
to this:
posts.forEach((post) => {
const module = new (require(`./routes/post/${post}`))(this);
const urlParams = module.params || "";
this.api.post(`/${post}${urlParams}`, async (req, res) => module.run(req, res))
});
So, if a given route wanted the extra URL param /:id added to it, then it would just define the .urlParams property on its exported module object to be `"/:id" and that would be automatically included in the route definition.
P.S. Most of the code in each of the branches of your switch statement in _loadHttpMethode() is identical. With a little factoring into a common function and one or two parameters passed to that function, you can eliminate all the copied code among those different branches of the switch so all each switch does is call one function and pass it a few arguments.
I generally would setup my express to handle this way in that case you want a dynamic insert. This is personal code so do make the adjustments necessary or observe the behavior! :)
WEBAPP.get('/room/:name', (req, res) => {
// Check if URL ends with / (in my case I don't want that)
if (req.url.endsWith('/')) return res.redirect('/');
// Check if URL param "name" matches my regex ex. Username1920 or redirect them
if (req.params.name.match(/^[a-zA-Z0-9]{3,24}$/) === null) return res.redirect('/');
// render the room (sending EJS)
res.render('room', {
title: req.params.name.toUpperCase()
});
});
/*
/*This example accepts one param and must follow my regex/rules*/
So if you were handed /room/test12345 your req.params.name would return a value. Notice the colon to define a param, so you could have /:room/:user/:request and it'd return:
req.params.room, req.params.user, req.params.request all defined! :)
How to seperate routes and parse request parameter in express API
You can just put all your different API methods for each model in a seperated folder, and then just parse the API routes in your main file.
Let's say we have one main file called app.js, and you can organize your API routes/endpoints in subfolders.
folder structure:
├── app.js
└── routes
└── api
└── users.js
users.js in the folder routes/api in this case contains all operations for your users endpoint, and you import this in your app.js file.
Based on the route examples defined below, this will let you parse your express API with these endpoints:
GET YOUR_API:PORT/users // fetch all users
GET YOUR_API:PORT/users/:userId // fetch single user by id
app.js
// this is just a demo app.js, please adapt to your needs
const express = require("express");
// express app
const app = express();
app.use(express.json());
// api routes
// endpoint No. 1, this will create the endpoint /users
// and will enable you to use all methods defined in file users.js
app.use("/users", require("./routes/api/users"));
// add more endpoints, example endpoint No. 2
app.use("/ENDPOINT_NAME", require("./routes/api/FILE_NAME"));
// handle everything else you need to handle in your main file
// run server
const PORT = process.env.PORT || 5000;
app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
in subfolder routes/api you add your api route files, like so:
routes/api/users.js
const express = require('express');
const router = express.Router();
// here we only GET all users and user by id,
// but you can add any endpoints required for your API.
// get all users
router.get('/', async (req, res) => {
try {
// do something to get all users
const allUsers = // fetch all users, depends on how you want to do it
return res.status(200).json(allUsers)
} catch (err) {
console.log(err)
return res.status(400).json({ msg: 'Bad request.' })
}
})
// get a specific user by Id from request parameters
router.get('/:userId', async (req, res) => {
try {
// user id from params
const userId = req.params.userId
// do something with this userId, for example look up in DB
return res.status(200).json({userId: `Requested userId is ${userId}`})
)
} catch (err) {
console.log(err)
return res.status(400).json({ msg: 'Bad request.' })
}
})
// add more user endpoints here
// with router.post, router.put, router.delete, whatever you need to do
module.exports = router
I'm attempting to build a MEAN app and trying to test POSTing with POSTMAN. When I do, I keep getting the dreaded "TypeError: Cannot read property 'name' of undefined". If I type in a simple string, the POST goes through fine. But when I use "req.body.name" I get the error. I've looked in every place and I'm not seeing my mistake. I even followed the suggestions on this thread with no luck. Any help or suggestions would be greatly appreciated.
Here's the code I am currently working with in my server.js file:
const express = require('express');
var bodyParser = require('body-parser');
var Bear = require('./models/bear')
var path = require('path');
var mongoose = require('mongoose');
var router = express.Router();
var app = express();
var staticAssets = __dirname + '/public';
app.use(express.static(staticAssets));
app.use('/api', router)
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
// Routes for my API
//===================================
// middleware to use for all requests
router.use(function(req,res,next){
// logging happens here
console.log('Something will happen.');
next(); // Head to the next router...don't stop here
});
// Test router to make sure everything is working (accessed at GET http://localhost:3000/api)
router.get('/', function(req, res){
res.json({message: 'hooray! welcome to our api!'})
})
//More routes will happen here with routes that end in "/bears"
router.route('/bears')
//Create a bear (accessed at POST http://localhost:3000/api/bears)
.post(function(req,res){
var bear = new Bear(); // Create a new instance of the bear model
console.log(req);
bear.name = req.body.name; // set the bears name (comes from the request)
//res.send(200, req.body);
bear.save(function(err){
if (err)
res.send(err);
res.json({message: 'Bear Created!!'});
});
});
//======================================
//var Products = require('./products.model.js');
var Product = require('./models/product.model');
var db = 'mongodb://localhost/27017';
mongoose.connect(db);
var server = app.listen(3000);
console.log("App is listening on port 3000");
Thanks.
Also, the url I'm trying to use inside of POSTMAN is http://localhost:3000/api/bears
Express processes requests Top-Down, meaning if you require a piece of functionality to be applied to all routes via middleware, than that middleware needs to be added to your app before any routes that require it. This is usually the case for middleware such as body-parser.
When using Router Middleware, you don't typically construct the router in the same file as the actual Express app that will use it as middleware. Instead, place it in a separate file and/or directory for organization purposes, this is considered a best practice.
Express Apps can be structured like so
/lib
/models
bear.js
product.js
/node_modules
/public
/css
/routes
api.js
package.json
server.js
The routes directory is where you would place any applicable Router Middleware files such as your api router. server.js is your main Express App and public is where your static assets are stored. lib is directory that contains any business logic files and models.
The actual Express app and Router files should look something like this
server.js
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
const path = require('path');
const apiRouter = require('./routes/api');
const app = express();
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, public)));
app.use(/api, apiRouter);
app.listen(port, () => {
console.log(`Listening on port ${port});
});
module.exports = app;
routes/api.js
'use strict';
const router = require('express').Router();
const Bear = require('./lib/models/bear');
router.use((req, res, next) => {
// logging happens here
console.log('Something will happen.');
next(); // Head to the next router...don't stop here
});
router.get('/', (req, res) => {
return res.json({ message: 'hooray! welcome to our api!'})
});
router.route('/bears')
//Create a bear (accessed at POST http://localhost:3000/api/bears)
.post((req, res) => {
var bear = new Bear(); // Create a new instance of the bear model
console.log(req);
bear.name = req.body.name; // set the bears name (comes from the request)
//res.send(200, req.body);
bear.save((err) => {
if (err)
return res.send(err);
return res.json({message: 'Bear Created!!'});
});
});
module.exports = router;
To note, you could break up your API even further to increase the amount of decoupling. An example of this would be to move the /api/bear route to its own router middleware and into its own route file. Then simply add it to your routes/api.js router as a middleware like you would in server.js. If your app is going to have a decent sized API, then this would be the best approach because it would allow the most flexibility when it comes to applying middleware to only certain routes and would make maintaining the source much easier.
I'm using a module called consign to include several different modules in a directory at once (instead of having a bunch of require statements). Within these modules, I've been setting the mount path for each endpoint at the top of the router file so as not to repeat it several times throughout the file. However, consign passes the same router to each of these (which should normally be fine) and the mount path is actually being overwritten via the use() method if the path is the same in any of the files. I'll try and show this the best way I can...
/routes/api.js
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;
/routes/api/player.js
module.exports = function (router) {
router.use('/player', router);
router.get('/getInfo', function (req, res, next) {
res.error = false;
res.data = {};
res.message = "Player getInfo API Call - IN DEVELOPMENT";
next();
});
};
/routes/api/profile.js
module.exports = function (router) {
router.use('/profile', router);
router.get('/getInfo', function (req, res, next) {
res.error = false;
res.data = {};
res.message = "Profile getInfo API Call - IN DEVELOPMENT";
next();
});
}
Consign is loading in the modules just fine, but the router.use() method seems to be overwriting the callbacks when the paths are the same (disregarding the base path that is). For instance, both "/player/getInfo" and "/profile/getInfo" work as a call, but are both responding with "/profile/getInfo" data.
BTW - in case you're wondering and in case it's pertinent, I have a small piece of middleware called "formatResponse" that will take the data and format all of the calls in the same way, which is why I have a "next()" instead responding from the function itself. The code for that is below as well.
/middleware/formateResponse.js
module.exports = function(req, res, next) {
res.json({
error: res.error,
data: res.data,
message: res.message
});
}
The way you're doing it right now, there's no scope. The fact that you mounted the router on '/profile' and then added a get statement to the '/getInfo' path doesn't really change the scope the way you think it does. They're both stored to match on '/getInfo', with the last one in winning (regardless of prefix. I bet navigating to http://your-server/getInfo will work too).
You either need to use a different router for each module (and then mount that one on the path root you want) or else be more explicit in the rest of the routes (e.g. call router.get('/profile/getInfo', ...).
I am using an express.js package called express-subdomain to facilitate requests to defined subdomains I set up.
As far as I understand, the subdomain constructor function expects an express router object which I pass to it from an exported router module.
What I have tried is as follows:
MAIN APP.JS SERVER FILE
var common = {
express: require('express'),
subdomain: require('express-subdomain')
};
common.app = common.express();
module.exports = common;
common.app.listen(3000, function () {
console.log(('app listening on http://localhost:3000'));
});
var router = require('./router/index');
// Error Handling
common.app.use(function(err, req, res, next) {
res.status(err.status || 500);
});
router/index
module.exports = function (){
var common = require('../app');
var router = common.express.Router();
common.app.get('/', function(req, res) {
res.send('Homepage');
});
common.app.use('/signup', require('./routes/signup'));
common.app.use(common.subdomain('login', require('./routes/login')));
}();
routes/login
var express = require('express');
var router = express.Router();
router.get('/', function (req, res) {
res.send('login working');
});
router.get('/info', function (req, res) {
});
module.exports = router;
I have tried to access the login subdomain at the following urls:
http://login.localhost
http://login.localhost:3000
http://login.localhost.com
http://login.localhost.com:3000
Any clarification or assistance appreciated.
author of express-subdomain here 👋
A couple of things:
Hosts must be setup correctly - I'd recommend something like so in your /etc/hosts file.
127.0.0.1 myapp.local
127.0.0.1 login.myapp.local
For more information on this see https://github.com/bmullan91/express-subdomain#developing-locally
Register the subdomain routes before any others, including the homepage route. The order is very important
The pattern you're using in /routes/index.js is not advised (requiring a self invoking function). Exporting the Router like you done in /routes/login.js is cleaner.
Finally, If you're still stuck take a look at the source for express subdomain and in particular its tests.
Happy coding.
I'm writing an API using Node.js and Express. My API has GET methods of the form:
/api/v1/doSomething
/api/v1/doSomethingElse
My code is looking something like this:
server.js:
var app = express();
...
var routes = require('./routes')
routes.attachHandlers(app, '/api/v1')
routes/index.js
...
module.exports.attachHandlers = function(app, context) {
//get a list of all the other .js files in routes
//for each route, require() it and call it myRoute
myRoute.attachHandlers(app, context)
}
routes/some-route.js
...
module.exports.attachHandlers = function(app, context) {
app.get(context + '/doSomething', doSomething)
app.get(context + '/doSomethingElse', doSomethingElse)
}
...
Effectively I'm passing the context path/mount point down through the app. If somebody were to write a route like the following, though, the context would be lost:
app.get('/doFoo', foo)
Rather than having that part of the API mounted on /api/v1/doFoo it's on /doFoo. I would like to avoid having to pass the context path around like this.
app.use supports mounting middleware on an optional mount path. I have seen references online to mounting an entire Express application on a mount path using app.use. This seems like the sort of thing I want to do, but I'm not sure how to do it or if it's the best solution for my particular use case.
To summarise - I want to mount my app.get() routes with a particular prefix by default. What's the best way of doing this?
With Express 4.0, the task is much cleaner with the Router. You can create as many routers as you need to nicely partition your app, and then attached them with app.use(). For example:
myapp.js
var express = require("express"),
router = express.Router(),
app = express(),
port = 4000;
// Here we declare our API which will be visible under prefix path
router.get('/', function (req, res) {
console.log("request to subspace hello");
res.send({ message: "Hi from subspace /api/v1/"});
});
// we attach our routes under /api/v1
app.use('/api/v1', router);
// here we have direct, root-level routing
app.get('/', function (req, res) {
console.log("request to rootspace hello");
res.send({message: "Hi from root /"});
});
app.listen(port);
console.log("App active on localhost:" + port);
Then run
node myapp.js
and visit
http://localhost:4000 and http://localhost:4000/api/v1
Here's a working example of mounting a route in Express 3:
./snipe3app.js
var express = require('express');
var app = module.exports = express();
app.get('/subapp', function (req, res) {
res.send('You are on the /sub/subapp page.');
});
./app.js
var express = require('express'),
http = require('http'),
subApp = require('./snipe3app'),
app = express();
app.use(express.favicon());
app.use(express.bodyParser());
app.use(app.router);
app.use('/sub', subApp);
app.get('/', function (req, res) {
res.send('You are on the root page');
});
http.createServer(app).listen(3000, function(){
console.log('Express server listening on port 3000. Point browser to route /secure');
});
You have to pay attention to the order in which the routes are handled when doing this.
I think express-namespace will work for this.