rest-handler v1.2.17
node-rest-handler
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 (therest
object). - 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
routeNotFound
listener.
NOTE: The underlying router is https://github.com/philidem/path-based-router/
Installation
npm install rest-handler --save
Usage
Create REST handler:
// Create instance of REST handler
var restHandler = require('rest-handler').create();
Add some middleware:
// middleware is added via "before" method call
restHandler
// Authenticate
.before(function(rest) {
rest.session = {
userId: 'john'
};
// go to next handler
rest.next();
})
// Authorize
.before(function(rest) {
if (rest.session.userId === 'john') {
// go to next handler
rest.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 logger
console.log(rest.req.method + ' ' + rest.req.url);
})
// Listener for not found
.on('routeNotFound', function(req, res) {
// just log missing route
console.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 URL
handler: function(rest) {
rest.res.setHeader('content-type', 'text/plain');
rest.send('Alive');
}
});
// Add a route with parameter placeholder
restHandler.addRoute({
// Route path (required)
// Placeholders (identified by path parts that start with ":") will be
// provided via rest.params
path: '/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 URL
handler: 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.
return rest.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 proceed
rest.next();
} else {
// send back error
rest.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 body
restHandler.addRoute({
path: '/order/:orderId',
method: 'POST',
// getParsedBody() will read all of the chunks of data and parse it as JSON
handler: 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 error
console.error(err);
return rest.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 body
restHandler.addRoute({
path: '/echo/body',
method: 'POST',
// getParsedBody() will read all of the chunks of data and parse it as JSON
handler: function(rest) {
// NOTE: you can also use req.on('data', function(data) {}) to
// read data chunks manually
rest.getBody(function(err, body) {
if (err) {
// log the error
console.error(err);
return rest.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 body
restHandler.addRoute({
path: '/echo/cookies',
// getParsedBody() will read all of the chunks of data and parse it as JSON
handler: function(rest) {
// rest.getCookies() will lazily parse the request cookies the first
// time the method is called.
var cookies = rest.getCookies();
// cookies will be JavaScript object with cookie names as keys
// and cookie values as corresponding value
rest.send(cookies);
}
});
Reading basic auth header:
// Echo basic auth
restHandler.addRoute({
path: '/echo/basic-auth',
// getParsedBody() will read all of the chunks of data and parse it as JSON
handler: 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)
var basicAuth = rest.getBasicAuth();
rest.send(basicAuth);
}
});
Error handling:
// Add a route that will send an error
restHandler.addRoute({
path: '/simulate-error',
handler: function(rest) {
rest.error(400, {
code: 'INVALID_REQUEST'
});
}
});
// Add error handler
restHandler.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 error
console.error(err);
// Output generic error message to end-user
rest.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 (typeof message !== "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 server
var server = require('http').createServer();
server.on('request', function(req, res) {
// Handle normal GET, POST, etc. requests
restHandler.handle(req, res);
});
server.on('upgrade', function(req, socket, head) {
// Handle web sockets
restHandler.handleUpgrade(req, socket, head);
});
// Listen on port 8080
server.listen(8080, function() {
});
7 years ago
9 years ago
9 years ago
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago
12 years ago