A module for creating a request handler that does REST style routing.
Unlike express, this request handler makes some simplifying assumptions:
- Routes have paths with simple placeholders or static paths (for anything more complicated, a different middleware can be added)
- Instead of passing around
req,res, andnext, these three properties are encapsulated in a single object (therestobject). - Route matching happens before any request handling so that all middleware and final route handler know which route matched. Requests that do not match any routes are a special case and can be handled via a
routeNotFoundlistener.
NOTE: The underlying router is https://github.com/philidem/path-based-router/
npm install rest-handler --saveCreate REST handler:
// Create instance of REST handlervarrestHandler=require('rest-handler').create();Add some middleware:
// middleware is added via "before" method callrestHandler// Authenticate.before(function(rest){rest.session={userId: 'john'};// go to next handlerrest.next();})// Authorize.before(function(rest){if(rest.session.userId==='john'){// go to next handlerrest.next();}else{rest.error(403,'Unauthorized.');}});Add some listeners:
// Listen for each request (unlike middleware, listeners are non-sequential)restHandler.on('beforeHandle',function(rest){// simple request loggerconsole.log(rest.req.method+' '+rest.req.url);})// Listener for not found.on('routeNotFound',function(req,res){// just log missing routeconsole.log('NOT FOUND: '+req.method+' '+req.url);});Add some routes:
restHandler.addRoute({// Route path (required)path: '/health/check',// Route method (optional, assumed to be all methods if not provided).// Allowed values:// - * (to match any method)// - Any legal HTTP method ("GET", "POST", "PUT", "PATCH", etc.)method: '*',description: 'Health check',// The "rest" argument will contains// - req: The raw incoming request as provided by NodeJS// - res: The raw outgoing response as provided by NodeJS// - url: The parsed URL object (see require('url').parse(...) NodeJS documentation)// - params: The parameters object// - route: The route that matched the request URLhandler: function(rest){rest.res.setHeader('content-type','text/plain');rest.send('Alive');}});// Add a route with parameter placeholderrestHandler.addRoute({// Route path (required)// Placeholders (identified by path parts that start with ":") will be// provided via rest.paramspath: '/orders/:orderId',// Route method (optional, assumed to be all methods if not provided)method: 'GET',description: 'Get order details',// The function that will be called for each request.// The "rest" argument will contains// - req: The raw incoming request as provided by NodeJS// - res: The raw outgoing response as provided by NodeJS// - url: The parsed URL object (see require('url').parse(...) NodeJS documentation)// - params: The parameters object// - route: The route that matched the request URLhandler: function(rest){if(!rest.params.orderId){// send error with 400 code// Request that will cause error: "http://localhost:8080/orders/"//// NOTE: Status code is optional. Calling error with one argument will// use the default error status code of 500.returnrest.error(400,'"orderId" is required');}// Request was something like "http://localhost:8080/orders/123"rest.send({id: rest.params.orderId,total: 123.55,items: [{itemNumber: 123},{itemNumber: 124}]});}});Route-specific "middleware":
restHandler.addRoute({path: '/top-secret',method: 'GET',// The before property can be a single function or an array of functions.// These function can allow the request to proceed by calling rest.next().before: [function(rest){if(rest.url.query.secretCode==='test'){// allow request to proceedrest.next();}else{// send back errorrest.error(403,'Access denied!');}}],handler: function(rest){rest.send({message: 'Congratulations! You have been allowed access.'});}});Reading request body:
// Example of reading JSON from request bodyrestHandler.addRoute({path: '/order/:orderId',method: 'POST',// getParsedBody() will read all of the chunks of data and parse it as JSONhandler: function(rest){// NOTE: you can also use req.on('data', function(data) {}) to// read data chunks manually and parse the resultant string.rest.getParsedBody(function(err,body){if(err){// log the errorconsole.error(err);returnrest.send(500,'Error reading request body');}// echo the body (body will be a JavaScript Object)rest.send(body);});}});// Example of reading raw text from request bodyrestHandler.addRoute({path: '/echo/body',method: 'POST',// getParsedBody() will read all of the chunks of data and parse it as JSONhandler: function(rest){// NOTE: you can also use req.on('data', function(data) {}) to// read data chunks manuallyrest.getBody(function(err,body){if(err){// log the errorconsole.error(err);returnrest.send(500,'Error reading request body');}// echo the body (body will be a String Object)rest.send(body);});}});Reading cookies:
// Example of reading raw text from request bodyrestHandler.addRoute({path: '/echo/cookies',// getParsedBody() will read all of the chunks of data and parse it as JSONhandler: function(rest){// rest.getCookies() will lazily parse the request cookies the first// time the method is called.varcookies=rest.getCookies();// cookies will be JavaScript object with cookie names as keys// and cookie values as corresponding valuerest.send(cookies);}});Reading basic auth header:
// Echo basic authrestHandler.addRoute({path: '/echo/basic-auth',// getParsedBody() will read all of the chunks of data and parse it as JSONhandler: function(rest){// rest.getBasicAuth() will lazily parse the request "authorization" header// using the "basic-auth-parser" module// (see https://github.com/mmalecki/basic-auth-parser)varbasicAuth=rest.getBasicAuth();rest.send(basicAuth);}});Error handling:
// Add a route that will send an errorrestHandler.addRoute({path: '/simulate-error',handler: function(rest){rest.error(400,{code: 'INVALID_REQUEST'});}});// Add error handlerrestHandler.errorHandler(function(rest,err){if(err.code==='INVALID_REQUEST'){// special handling for requests that were given code of "INVALID_REQUEST"rest.res.setHeader('Content-Type','text/html');rest.send('<html><body>Invalid request</body></html>');}else{// Log the errorconsole.error(err);// Output generic error message to end-userrest.send(500,'Unknown error occurred');}});Override notFound handler:
// By default the notFound handler will send the status message as plaintext with a 404 status code.// You can override this behavior using the `onRouteNotFound` option.require('rest-handler').create({onRouteNotFound: function(message,req,res,socket){if(typeofmessage!=="string"){// Message is optional.res=req;req=message;}// Respond however you would like.res.statusCode=200;res.end("Hello World");}})Start an http server and delegate handling of requests to REST handler:
// Create standard HTTP servervarserver=require('http').createServer();server.on('request',function(req,res){// Handle normal GET, POST, etc. requestsrestHandler.handle(req,res);});server.on('upgrade',function(req,socket,head){// Handle web socketsrestHandler.handleUpgrade(req,socket,head);});// Listen on port 8080server.listen(8080,function(){});