0.3.0 • Published 5 years ago

apigateway-path-match v0.3.0

Weekly downloads
-
License
MPL-2.0
Repository
gitlab
Last release
5 years ago

apigateway-path-match

A javascript router for AWS Lambda

Install apigateway-path-match via npm:

$ npm install apigateway-path-match

Define your routes using the set(method, path, value) function:

const PathMatcher = require('apigateway-path-match');
const router = new PathMatcher();

router.set("GET", "/foo",        "Getting foo");
router.set("GET", "/foo/{id}",   "Getting one foo");
router.set("ANY", "/foo/{any+}", "Catch-all foo");

Then lookup particular requests against your routes using the match(method, path) function:

var m;
m = router.match("GET", "/foo/123");        // m is "Getting one foo"
m = router.match("DELETE", "/foo/bar/baz"); // m is "Catch-all foo"
m = router.match("GET", "/foobar");         // m is null, because no route matched

In typical usage, you wouldn't use strings for values; you'd use route handlers. Here's how that looks like in an AWS Lambda function:

const PathMatcher = require('apigateway-path-match');
const router = new PathMatcher();

router.set("GET", "/foo", (event, context, callback) => {
    callback(null, { statusCode: 200, body: "This is foo" });
});
router.set("GET", "/bar", (event, context, callback) => {
    callback(null, { statusCode: 200, body: "This is bar" });
});
// Set more routes here...

const notFound = (event, context, callback) => {
    callback(null, { statusCode: 404, body: "Not Found" });
};

exports.handler = (event, context, callback) => {
    const routeHandler = router.match(event.httpMethod, event.path);
    if (routeHandler) {
        return routeHandler(event, context, callback);
    }

    return notFound(event, context, callback);
};

Matching Rules

Path patterns are made up of "/"-separated components. Each component can be either

  1. A literal value, e.g. "foo" or "";
  2. A wildcard, if it starts with "{" and ends with "}", e.g. "{foo}";
  3. A super-wildcard, if it starts with "{" and ends with "+}", e.g. "{foo+}".

A wildcard will match anything except "/", i.e. it will accept any value for this path component. A super-wildcard will match anything including "/", i.e. it will accept any value for the remainder of the path. Wildcard and super-wildcard are equivalent to respectively the * and ** patterns used in globbing libraries like minimatch.

It's meaningless for a path pattern to have any components following a super-wildcard. set will throw an Error if you try to create one. For example, the following path patterns are illegal:

  • /{foo+}/bar
  • /foo/{bar+}/{baz}
  • /foo/{bar+}/{baz+}

Literal matches have higher precedence than wildcard matches, which have higher precedence than super-wildcard matches. E.g.

router.set("GET", "/foo/bar/baz",  "Literal");
router.set("GET", "/foo/{id}/baz", "Wildcard");
router.set("GET", "/foo/{any+}",   "Super-wildcard");

router.match("GET", "/foo/bar/baz");  // "Literal"
router.match("GET", "/foo/123/baz");  // "Wildcard"
router.match("GET", "/foo/bar/quux"); // "Super-wildcard"

All paths must start with "/", but the leading slash is ignored (there is no literal empty string path component before it). set will throw an Error if the path pattern doesn't start with "/".

In addition to a path pattern, each route added to PathMatcher has a method. This can be any HTTP method, case-insensitive (e.g. "GET", "Post", etc). Multiple methods can be given at once in an array. As a special case, the "ANY" method acts as a fallback which matches any method. It has lower precedence than explicitly specified methods, e.g.

router.set(["GET", "PUT"], "/foo/{id}",  "Explicit method");
router.set("ANY",          "/foo/{id}",  "Catch-all method");

router.match("PUT", "/foo/123"); // "Explicit method"
router.match("POST", "/foo/123"); // "Catch-all method"

These rules are based on my understanding of how Amazon API Gateway works, based on the developer guide.

Caveats

No consistency checks. If you set the same pattern more than once, the earlier values are silently overwritten by the latest one:

router.set("GET", "/foo", "Original value");
router.match("GET", "/foo"); // "Original value"

router.set("GET", "/foo", "New value");
router.match("GET", "/foo"); // "New value"

No uniqueness guarantee. You can store any kind of value in your router; this library never checks them. Multiple patterns can point to the same value. When you perform a match, there's no way to know which pattern matched. If this information is important to you, make sure all your values are unique, such that the pattern to value mapping is one-to-one.

Ambiguity. When a match fails, this library doesn't tell you whether it's because of the path or the method. If this distinction is important to you, you can use wildcard methods to catch cases where the path matches, but the method doesn't, e.g.

var routes = [
    ["GET",  "/foo", "Get foos"],
    ["POST", "/foo", "Create a foo"],
];

for (let [method, pathPattern, value] of routes) {
    router.set(method, pathPattern, value);
    router.set("ANY", pathPattern, "Method didn't match");
}
0.3.0

5 years ago

0.2.0

5 years ago

0.1.1

5 years ago