I am not really sure what to title this, but I'm new to Node.js. I just found a neat REST API project on GitHub to implement but I'm not sure how I can split all GET and POST etc. to separate files.
I have one singular api.js file where I have
function API_ROUTER(router, connection, md5) {
var self = this;
self.handleRoutes(router, connection, md5);
}
API_ROUTER.prototype.handleRoutes = function(router, connection, md5) {
router.get("/", function(req, res) {
res.json({"Message" : "Hello World !"});
});
};
module.exports = API_ROUTER;
Now how can I create a sibling other.js and use:
var api = require('./api.js');
// Create router.get, router.post etc. here?
but I'm not sure how I can split all GET and POST etc. to separate files.
One way you can organize your routes would be to have a separate object for each route that has the handlers (separated by HTTP methods) and other needed info such as the path:
api/home.js
module.exports = {
path: '/',
handlers: {
'get': function(req, res) {
res.json({"Message" : "Hello World !"});
},
'post': {
// ...
}
// ...
}
}
api/other.js
module.exports = {
path: '/other',
handlers: {
'get': function(req, res) {
res.json({"Message" : "Other !"});
},
// ...
Then you can load all of these inside the handleRoutes method:
API_ROUTER.prototype.handleRoutes = function(router, connection, md5) {
var routes = ['home', 'other'];
routes.forEach(function(name) {
// load the current route object (NOTE: you should use the path module for determining file path in a cross-platform manner)
var routeObject = require('./' + name + '.js');
var apiPath = routeObject.path;
var handlers = routeObject.handlers;
var methods = Object.keys(handlers);
// assign handlers for each method
methods.forEach(function(method) {
router[method](apiPath, handlers[method]);
});
});
};
This will install all your routes with the appropriate information and handlers.
Now you can call this code by instantiating your API_ROUTER with the necessary data:
// initialize the api (and handle the routes internally)
var Api = new require('./api.js')(router, connection, md5);
If you implement a RESTful API, then you should keep in mind that this is just one way how you can provide data, and you might want to change it in future, as of that the API will most of the time only be a translation layer.
Normally you will split your code based on the resources, and the code that is handling the request won't have so much logic, it will just take the request and pass it to you internal API. For that purpose you not really need an additional layer if you already use express.js or a similar library.
In express the app.use([path,] function [, function...]), already provides the functionality you would need to modularize your code. For each resource your will create an own express.Router that itself also might mount another sub module. So for this part you do not really need a library.
When might a library be useful:
if it automatically translates thrown errors to the correct response codes
if it includes a tool to automatically create a documentation to your API
if it fully abstracts the underlaying routing system so that you can hook into express, hapi, ... without the need to change the code.
Here how a setup with express.js could look like
./lib/rest/customer.js
var customerSystem = require('../customer-system');
var express = require('express');
var router = new express.Router();
router.get('/:id', function(req, res, next) {
customerSystem.find({
id: req.params.id
}, function(err, customer) {
if (err) {
res.status( /*correct status code*/ ).send( /*depending on the api return json, xml, ....*/ )
} else {
res.send( /*depending on the api return json, xml, ....*/ )
}
})
});
router.delete('/:id', function(req, res, next) {
customerSystem.delete({
id: req.params.id
}, function(err) {
//...
});
});
router.post('/', function(req, res, next) {
//...
});
//save the customer id for the pass to the sub routers
router.use('/:id', function(req, res, next) {
req.customerId = req.params.id;
next();
});
router.use('/:id/addresses', require('./customer-address') )
module.exports = router;
./lib/rest/customer-address.js
var customerSystem = require('../customer-system');
var express = require('express');
var router = new express.Router();
router.get('/:id', function(req, res, next) {
customerSystem.find({
id: req.customerId
}, function(err, customer) {
// ...
})
});
/* ..... */
//save the address id for the pass to the sub routers
router.use('/:id', function(req, res, next) {
req.addressId = req.params.id;
next();
});
router.use('/:id/addresses', require('./customer-address') )
module.exports = router;
Related
I have the following case:
There is a list of routes in the form
var lst = ["route1/:foo", "route2/:bar", "route3/:bar/route4/:baz", ..] // this list has like 200 entries
I have the following code
app.use(lst, function (req, res) {
// here I want to know which route the middleware was invoked on
// req.route.path doesn't work unless I use app.METHOD()
// req.originalUrl would give me route1/200, instead of route1/:foo
})
What I tried so far:
Using the router stack as in app._router.stack, my routes aren't even registered there - I don't see route1/:foo, route2/:bar and route3/:bar/route4/:baz
Hook into the express router:
var Route = express.Route;
let defaultImplementation = Route.prototype.dispatch;
function foo(req, res) {
console.log('Called route ', req.route.path); // still doesn't trigger on the routes in lst, only parent routes
}
Route.prototype.dispatch = function handle(req, res, next) {
foo(req, res); // req.route is available here
defaultImplementation.call(this, req, res, next);
};
By the way, I'm passing those routes and using them along with http-proxy-middleware https://github.com/chimurai/http-proxy-middleware, so if you have any clues on how do achieve that with that library as well, I'd be very grateful as I couldn't find out.
I think passport.js is a amazing framework. Unfortunately, however, it seems to be that doesn't support socket. Why I said this is that Sails framework provides http and socket. When user connected sails's service through passport.js, it doesn't matter. Accessing by socket makes error. Because socket may not support middleware?
Anyway, the critical problem, I don't know how apply passport.js on socket.
Indeed, the websocket requests do not pass threw the passport middleware, but it is possible to use a workaround. Are you using this sails passport generator ?
I added this code to the passport policy to add passport methods to the socket requests.
/** Content not generated BEGIN */
var http = require('http')
, methods = ['login', 'logIn', 'logout', 'logOut', 'isAuthenticated', 'isUnauthenticated'];
/** Content not generated END */
module.exports = function (req, res, next) {
// Initialize Passport
passport.initialize()(req, res, function () {
// Use the built-in sessions
passport.session()(req, res, function () {
// Make the user available throughout the frontend
res.locals.user = req.user;
/** Content not generated BEGIN */
// Make the passport methods available for websocket requests
if (req.isSocket) {
for (var i = 0; i < methods.length; i++) {
req[methods[i]] = http.IncomingMessage.prototype[methods[i]].bind(req);
}
}
/** Content not generated END */
next();
});
});
};
Alexis gave the right answer... I think it's the way recommanded by Mike, regarding this message : https://stackoverflow.com/a/17793954/6793876
Just delete passport's mentions in config/http.js , make new policy passportMiddleware.js with the following content :
//passportMiddleware.js
var passport = require('passport');
var http = require('http');
module.exports = function (req, res, next) {
// Initialize Passport
passport.initialize()(req, res, function () {
// Use the built-in sessions
passport.session()(req, res, function () {
res.locals.user = req.user;
var methods = ['login', 'logIn', 'logout', 'logOut', 'isAuthenticated', 'isUnauthenticated'];
if (req.isSocket) {
for (var i = 0; i < methods.length; i++) {
req[methods[i]] = http.IncomingMessage.prototype[methods[i]].bind(req);
}
}
next();
});
});
};
And finally add this policy to all controllers, in policies.js :
module.exports.policies = {
RabbitController: {
nurture : ['passportMiddleware','isRabbitMother'],
feed : ['passportMiddleware','isNiceToAnimals', 'hasRabbitFood']
}
};
I am currently working on a project to develop an API manager to control an existing API.
It contains a list of "before" and "after" middlewares, which are used to do things like security checking and logging. And a "service" middleware to do http request to the existing API. But the problem is that I want to make the order the middleware being executed to be dynamic, meaning that I could load some configuration file to change the order the middleaware get executed every time the request comes in.
here is my previous code:
'use strict';
// Loading the express library
var express = require('express');
var app = express();
var service = require('./routes/index');
// Testing configurable middleware
var confirguration = {
before1: {
priority: 100,
enable: true
},
before2: {
priority: 80,
enable: true
},
service: {
priority: 50,
enable: true
},
after1: {
priority: 30,
enable: true
},
after2: {
priority: 10,
enable: true
}
}
var before1 = require('./example_middleware/before1');
var before2 = require('./example_middleware/before2');
var after1 = require('./example_middleware/after1');
var after2 = require('./example_middleware/after2');
// Fake request to simulate the /service
var fakeRequest = require('./example_middleware/fake_request');
// Function to sort the order of the middleware to be executed
var sortConfig = function(confirguration){
var sortable = [];
for (var middleware in confirguration)
// To make middlewares configurable
if (confirguration[middleware]['enable'] == true){
sortable.push([middleware, confirguration[middleware]['priority']]);
}
sortable.sort(function(a, b) {return b[1] - a[1]});
return sortable;
}
// var sortedConfig = [];
var middlewareSet = new Array();
app.use('/test', function(request, response, next){
var middleware;
var sortedConfig = sortConfig(confirguration);
for (var i in sortedConfig){
switch(sortedConfig[i][0]){
case 'before1':
middleware = before1;
break;
case 'before2':
middleware = before2;
break;
case 'service':
middleware = fakeRequest;
break;
case 'after1':
middleware = after1;
break;
case 'after2':
middleware = after2;
break;
}
// console.log(sortedConfig[i][0]);
// Execute the middleware in expected order
middlewareSet.push(middleware);
}
// request.sortedConfig = sortedConfig;
console.log(middlewareSet);
console.log('middleware list sorted');
next();
});
app.use('/test', middlewareSet);
But I keep getting the same error message coming from the app.use() at the last line:
app.use() requires middleware functions
It works if I use:
app.use('/test', [before1, before2, fakeRequest, after1, after2]);
But it's not dynamic though, what did I misunderstand? There must be a way to do this in express.js.
Thanks in advance.
EDIT:
I modified my code according to Ryan's answer, here is the code:
var async = require('async');
app.use('/test', configurableMiddleWare);
function configurableMiddleWare(req, res, next) {
var operations = [];
var middleware;
var sortedConfig = sortConfig(confirguration);
// push each middleware you want to run
sortedConfig.forEach(function(fn) {
switch(fn[0]){
case 'before1':
middleware = before1;
break;
case 'before2':
middleware = before2;
break;
case 'service':
middleware = fakeRequest;
break;
case 'after1':
middleware = after1;
break;
case 'after2':
middleware = after2;
break;
}
operations.push(middleware); // could use fn.bind(null, req, res) to pass in vars
});
console.log('middleware list sorted');
// now actually invoke the middleware in series
async.series(operations, function(err) {
if(err) {
// one of the functions passed back an error so handle it here
return next(err);
}
// no errors so pass control back to express
next();
});
}
Just to make sure I haven't made any mistakes in my test middleware, here is an example of one of them:
'use strict';
var express = require('express');
var router = express.Router();
router.route('/')
.all(function(request, response, next){
console.log('This is middleware BEFORE1');
next();
});
module.exports = router;
Now, when I run my application, I got the following error from npm:
TypeError: Cannot call method 'indexOf' of undefined
at Function.proto.handle (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/express/lib/router/index.js:130:28)
at router (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/express/lib/router/index.js:35:12)
at /Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:610:21
at /Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:249:17
at iterate (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:149:13)
at async.eachSeries (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:165:9)
at _asyncMap (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:248:13)
at Object.mapSeries (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:231:23)
at Object.async.series (/Users/jialunliu/Documents/SOA_project/FAT-LADY/node_modules/async/lib/async.js:608:19)
at configurableMiddleWare (/Users/jialunliu/Documents/SOA_project/FAT-LADY/app.js:135:11)
Which is coming from the line
async.series(operations, function(err){})
I am keep getting this kind of error message, saying the function could not read from this array of functions "operations"....
I think you are on the right track, you will just need to tweak a few things. I would register one top level function with app.use() and then do all of your dynamic stuff within that function. Updating my answer to a working example. Be sure to install async first npm install --save async
// define all middleware functions
var middleware = {
mw1: function(req, res, next) {
console.log('mw 1');
next();
},
mw2: function(req, res, next) {
console.log('mw 2');
next();
},
mw3: function(req, res, next) {
console.log('mw 3');
next();
},
mw4: function(req, res, next) {
console.log('mw 4');
next();
}
};
// register our "top level function"
app.use(configurableMiddleware);
var requestCount = 1; // this is just for the working example
function configurableMiddleware(req, res, next) {
var isEvenRequest = requestCount++ % 2 === 0; // simple logic to alternate which "configurable" middleware to use
var operations; // in the real world you could build this array dynamically, for now we just hardcode two scenarios as an example
// Each request to http://localhost:3000 will alternate which middleware is used, so you will see a different log each time
if(isEvenRequest) {
console.log('Even request should log mw2 and mw4');
// .bind(null, req, res) makes sure that the middleware gets the request and response objects when they are invoked,
// as of this point they still haven't been invoked...
operations = [middleware.mw2.bind(null, req, res), middleware.mw4.bind(null, req, res)];
}
else {
console.log('Odd request should log mw1 and mw3');
operations = [middleware.mw1.bind(null, req, res), middleware.mw3.bind(null, req, res)];
}
// invoke each middleware in series - you could also do async.parallel if the order of middleware doesn't matter
// using the async module: https://github.com/caolan/async
async.series(operations, function(err) {
if(err) {
console.log('There was a problem running the middleware!');
return next(err);
}
// all middleware has been run
next();
});
}
For more info on .bind() see https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind
connect-sequence: a dedicated node module for that specific purpose:
You can just use the module connect-sequence which is designed for that purpose:
npm install --save connect-sequence
see:
the npmjs page: https://www.npmjs.com/package/connect-sequence
or the github project: https://github.com/sirap-group/connect-sequence
and then, here an example of usage:
/**
* Product API
* #module
*/
var ConnectSequence = require('connect-sequence')
var productsController = require('./products.controller')
module.exports = productRouter
function productRouter (app) {
app.route('/api/products/:productId')
.get(function (req, res, next) {
// Create a ConnectSequence instance and setup it with the current `req`,
// `res` objects and the `next` callback
var seq = new ConnectSequence(req, res, next)
// build the desired middlewares sequence thanks to:
// - ConnectSequence#append(mid0, ..., mid1),
// - ConnectSequence#appendList([mid0, ..., mid1])
// - and ConnectSequence#appendIf(condition, mid)
if (req.query.filter) {
seq.append(productsController.filter)
}
if (req.query.format) {
seq.append(
productsController.validateFormat,
productsController.beforeFormat,
productsController.format,
productsController.afterFormat
)
}
// append the productsController.prepareResponse middleware to the sequence
// only if the condition `req.query.format && req.formatedProduct` is true
// at the moment where the middleware would be called.
// So the condition is tested after the previous middleware is called and thus
// if the previous modifies the `req` object, we can test it.
seq.appendIf(isProductFormatted, productsController.prepareResponse)
seq.append(productsController.sendResponse)
// run the sequence
seq.run()
})
app.param('productId', function (req, res, next, id) {
// ... yield the product by ID and bind it to the req object
})
function isProductFormatted (req) {
return Boolean(req.formatedProduct)
}
}
This is open source, PR are welcome!
If you like and use connect-sequence, but if you find bug or need some new features, feel free to post issues or submit pull requests!
Based on the idea behind #Ryan's code I came up with this function. It executes a list of middleware in order binding the variables as needed, allowing everything to be executed by just executeMiddlewareList([middleware1, middleware2...], req, res, next);. For each middlewarereq, res is passed and the callback from async.eachSeries. This means when next() is called inside the middleware, then next one will be handled from the list. If middleware throws an error with next(err), execution will stop and you can manually handle this.
function executeMiddlewareList (middlewareList, req, res, next) {
async.eachSeries(middlewareList, function(middleware,callback) {
middleware.bind(null,req,res,callback)()
}, function(err) {
if (err) return res.status(500).json({error: err});
next();
})
}
function testMid (number) {
return function (req, res, next) {
log.debug('req.test from', req.test, " to ", number);
req.test=number;
next();
}
}
router.get('/test', function(req, res, next) {
m.executeMiddlewareList([test(1), test(2)], req, res, next);
//Output: req.test from undefined to 1
// req.test from 1 to 2
}, function (req,res) {
//Do stuff after the executeMiddlewareList, req.test = 2
})
Finally, I find the answer according the Ryan's, the code would look like this:
function configurableMiddleWare(req, res, next) {
var operations = [];
var middleware;
var sortedConfig = sortConfig(confirguration);
// push each middleware you want to run
sortedConfig.forEach(function(fn) {
switch(fn[0]){
case 'before1':
middleware = before1;
break;
case 'before2':
middleware = before2;
break;
case 'service':
middleware = fakeRequest;
break;
case 'after1':
middleware = after1;
break;
case 'after2':
middleware = after2;
break;
}
console.log(fn[0]);
console.log(middleware);
operations.push(middleware.bind(null, req, res)); // could use fn.bind(null, req, res) to pass in vars
});
console.log('middleware list sorted');
// now actually invoke the middleware in series
async.series(operations, function(err) {
if(err) {
// one of the functions passed back an error so handle it here
return next(err);
}
console.log('middleware get executed');
// no errors so pass control back to express
next();
});
}
app.use('/test', configurableMiddleWare);
The key step is indeed the operations.push(middleware.bind(null, req, res));
Which to be honest, I don't understand what does it mean exactly. I know this is passing "req" and "res" variable into the middleware, but I don't get what the point of the "null" in the front. Much appreciated if someone could help me clarify this.
Ok, this should be easy for somebody to point out.
I checked the other similar questions and none helped.
I'm trying to move all my routes to a separate routes.js file. In it I have:
module.exports = function (app) {
var user = {
list : require('./routes/user.js')
}
, index = {
index : require('./routes/index.js')
}
app.get('/', function(request, response){
response.send('You made it to the home page.')
});
app.get('/users', user.list);
}
And in my app.js file I have this:
var register_routes = require('./routes.js')
register_routes(app)
My index route works fine, but it kicks back on app.get('/users', user.list); with this error:
.get() requires callback functions but got a [object Object]
This is an out of the box express app so theres not too much to describe.
Thanks.
EDIT: Per request, here is what is in ./routes/user.js :
exports.list = function(req, res){
res.send("respond with a resource");
};
You export an object with the key list having the your function as value.
So to access your function you would need to do this require('./routes/user.js').list
Or with your code user.list.list.
To solve this you have two possibilities.
Either write:
var user = {
list : require('./routes/user.js').list
}
Or:
module.exports = function(req, res){
res.send("respond with a resource");
};
EDIT
If your routes/user.js will probably later look like this:
module.exports.list = function(req, res){
res.send("respond with a resource");
};
module.exports.delete = function(req, res){
res.send("delete user");
};
If yes then you can just write it that way in your routes.js:
var user = require('./routes/user.js');
I think what you want is:
module.exports = function (app) {
var user = {
list : function(request, response){
require('./routes/user.js');
}
}
}
, index = {
index : function(request, response){
require('./routes/index.js')
}
}
app.get('/', function(request, response){
response.send('You made it to the home page.')
});
app.get('/users', user.list);
}
In this way give a callback to the route and this callback execute the require.
If you are using router in your application for all routing purpose,
var express = require('express');
var router = express.Router();
var index = require('./index');
/* GET home page. */
router.get('/', index.list);
module.exports = router;
then in your index.js file, just do
router.list = function(req, res){
res.send("respond with a resource");
};
After so many time seeking around in web, I found something.
First of all, You instantiate the code like this on another file, (e.g.: humancomms.ts):
import express from 'express';
export function shout(request: express.Request, response: express.Response, next: () => void) {
response.send('Shout so loud!');
}
export function speak(request: express.Request, response: express.Response, next: () => void) {
response.send('Speak less loud!');
}
What exactly this code does? Nobody knows.(Hehe, just kiddin')
This make a middleware functions to separate from main server file to... organize, of course.
And how We can use it? Just like this (inside your server file):
const shout = require('./humancomms').shout;
const speak = require('./humancomms').speak;
app.use('/shout', shout);
app.use('/speak', speak);
This code takes all middlewares funcs and executes when some path is called.
This not solve all problems like multi path to same type, as if You want call differently intensities of shout ('/shout/high?asd=asd', '/shout/low?asd=asd'), but there is a catch You can try on secondary file:
import express from 'express';
export function shout(request: express.Request, response: express.Response, next: () => void) {
if (request.path.includes('/high')) {
response.send('Shout so loud!');
} else if (request.path.includes('/low')) {
response.send('Really shout?');
}
}
Look at this good article about:
TypeScript Express tutorial #1. Middleware, routing, and controllers (https://wanago.io/2018/12/03/typescript-express-tutorial-routing-controllers-middleware/)
and the official documentation website:
Writing middleware for use in Express apps (http://expressjs.com/en/guide/writing-middleware.html)
I am a newbie in Node.js (and Express) and I am trying to make sense of this. Say I have a website with 3 pages (can be GET or POST): /, /page1, /page2. What should I do so that every page is handled by a separate JS file?
app.all('/', function(request, response)
{
// Get home.js to handle this request and response
});
app.all('/page1', function(request, response)
{
// Get page1.js to handle this request and response
});
app.all('/page2', function(request, response)
{
// Get page2.js to handle this request and response
});
Better yet, is there a way to define a wildcard so there is not so much repetition? Something like this:
app.all('*', function(request, response)
{
// Get *.js to handle this request and response. * is whatever the URI string is
});
The trick here is that app is local to the file that creates it. So you have to get that object to the scope of the other files.
Each other file should export a funciton that you can pass your app instance to so it can register new routes. An approach like this should work.
// home.js
exports.register = function(app) {
app.all('/', function(request, response) { ... });
};
// page1.js
exports.register = function(app) {
app.all('/page1', function(request, response) { ... });
};
// page2.js
exports.register = function(app) {
app.all('/page2', function(request, response) { ... });
};
//server.js - setup the app
app = express.createServer();
require('./home').register(app);
require('./page1').register(app);
require('./page2').register(app);
And for the second part of your question, you want to share some setup methods?
app.all('*', function(req, res, next) {
res.header 'x-snazzy-header', 'Im so snazzy'
next()
});
app.all('/page/:id', function(req, res) {
res.send('content for page #'+ req.params('id'));
});
First, you can use * or named params like /users/:id, to match a range of routes. And if you want to do some common setup, you can actually execute 2 routes. The route handler takes an optional third argument next. When invoked, it will try to find the next route to match. So you can setup things like common headers for a bunch of routes with it.
Continuing my discussion with #Alex. Here's how I did it. Any gotcha?
// app.js
var EXPRESS = require('express');
var URL = require('url');
var PATH = require('path');
var app = EXPRESS.createServer();
app.all(/^\/([a-zA-Z0-9-]+)$/, function(request, response, next)
{
var page = request.params[0];
if (PATH.existsSync(__dirname + '/' + page + '.js'))
{
require('./' + page).handleRequest(request, response, next);
}
else
{
next();
}
});
app.all('*', function(request, response)
{
response.send('Catch all');
});
// --- truncated for brievity
// page1.js
exports.handleRequest = function(request, response, next)
{
response.send('Howdy!');
};