npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

rest-handler

v1.2.17

Published

A module for creating a request handler that does REST style routing

Downloads

758

Readme

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() {
    
});