1.2.17 • Published 7 years ago

rest-handler v1.2.17

Weekly downloads
1,997
License
-
Repository
github
Last release
7 years ago

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, and next, these three properties are encapsulated in a single object (the rest 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() {
    
});
1.2.17

7 years ago

1.2.16

9 years ago

1.2.15

9 years ago

1.2.14

9 years ago

1.2.13

9 years ago

1.2.12

9 years ago

1.2.11

10 years ago

1.2.10

10 years ago

1.2.9

10 years ago

1.2.8

10 years ago

1.2.7

10 years ago

1.2.6

10 years ago

1.2.5

10 years ago

1.2.4

10 years ago

1.2.3

10 years ago

1.2.2

10 years ago

1.2.1

10 years ago

1.2.0

10 years ago

1.1.29

10 years ago

1.1.28

10 years ago

1.1.27

10 years ago

1.1.26

10 years ago

1.1.25

10 years ago

1.1.24

10 years ago

1.1.23

11 years ago

1.1.22

11 years ago

1.1.21

11 years ago

1.1.20

11 years ago

1.1.19

11 years ago

1.1.18

11 years ago

1.1.17

11 years ago

1.1.16

11 years ago

1.1.15

11 years ago

1.1.13

11 years ago

1.1.12

11 years ago

1.1.11

12 years ago

1.1.10

12 years ago

1.1.9

12 years ago

1.1.8

12 years ago

1.1.7

12 years ago

1.1.6

12 years ago

1.1.5

12 years ago

1.1.4

12 years ago

1.1.3

12 years ago

1.1.2

12 years ago

1.1.1

12 years ago

1.1.0

12 years ago

1.0.0

12 years ago