@n7e/http-routing
TypeScript icon, indicating that this package has built-in type declarations

0.1.0 • Public • Published

HTTP Routing

Provides HTTP routing middleware.

For further insights, read on.

Installation

To install this library use your favorite package manager. No additional steps are required to start using the library.

npm install @n7e/http-routing

This library is implemented in TypeScript but can be used with JavaScript without any additional steps.

Action Router Middleware

The provided action router middleware delegates incoming requests to a matching routable action. To create an action router middleware use the provided action router middleware builder.

Action Router Middleware Builder

To create an action router middleware builder instance, simply import it and create a new instance:

import { ActionRouterMiddlewareBuilder } from "@n7e/http-routing";

const actionRouterMiddlewareBuilder = new ActionRouterMiddlewareBuilder();

If we already have or want to create a routable action we can add it to the builder as is:

actionRouterMiddlewareBuilder.addRoutableAction(routableAction);

Otherwise, the builder provides a method to create a routable action from provided parameters:

import { RequestMethod } from "@n7e/http";

actionRouterMiddlewareBuilder.add(
    RequestMethod.GET,
    "some/path",
    (request, parameters, signal) => { /* Produce an appropriate response. */ }
);

Once configured the builder will produce a middleware that will match incoming requests to the registered actions if possible. If no matching action is found the middleware will delegate to the provided request handler.

const actionRouterMiddleware = actionRouterMiddlewareBuilder.build();

How requests are matched and actions invoked is decided by the builders configured routing predicate and routable action invokation respectively.

Routable Action

Routable actions are the destination of incoming requests and are responsible for producing appropriate HTTP responses. To match routable actions against incoming requests routable actions has the following properties (besides the action itself of course):

requestMethod
HTTP request method to match against.
pathExpression
A regular expression to match against the HTTP URI path component.
metadata
Arbitrary metadata. This can be used to match requests or any other purpose the implementor feel is appropriate.

Routing Predicate

The logic behind how requests are matched against routable actions can be configured by providing a predicate function to the builder:

actionRouterMiddlewareBuilder.useRoutingPredicate();

The predicate function receives the incoming request and a routable action and should determine whether the request should be routed to the given routable action.

The default behaviour is to check if the request method matches the routable action request method and to test that the routable action path expression matches the request URI path.

Here is an example of a routing predicate that matches request methods defined in method override request headers.

actionRouterMiddlewareBuilder.useRoutingPredicate(
    (request, routableAction) => (
        [
            request.method,
            request.headerFieldValueFor("X-Http-Method-Override")
        ].includes(routableAction.requestMethod) &&
        routableAction.pathExpression.test(request.uri.path)
    )
);

Routable Action Invocation

How routable actions are invoked once an incoming requests has been routed can be configured by providing a routable action invokation function to the builder:

actionRouterMiddlewareBuilder.useActionInvocation(invokationFunction);

The function receives the incoming request, routable action and an abort signal and should invoke the routable action's action to produce an appropriate response.

The default behaviour is to extract parameters from the incoming request URI path using the action router's path expression and invoke the action with the request, parameters and abort signal.

See path pattern for how to extract parameters from route expressions.

Path Pattern

When creating routable actions using the action router middleware builder or using decorators you can use a special syntax to define which URI path components a routable action should match against.

It looks really similar to a URI path component but with an additional leading : for parameters, a trailing ? for optional parameters and an optional regular expression enclosed in parentheses.

Here are some examples:

Description Path Pattern
Match path verbatim some/path
Extract parameter some/:parameter
Extract optional parameter some/:parameter?
Extract parameter matching expression some/:parameter(test-.+?)
Extract optional parameter matching expression some/:parameter(test-.+?)?

Regular expressions used in path patterns are quite limited, most notably anchors and capture groups are not supported.

Path patterns can be converted to regular expressions using a provided function:

import { pathExpressionFor } from "@n7e/http-routing";

const regularExpression = pathExpressionFor("/some/:parameter");

This will produce a regular expression that matches URI path components appropriately and creates named groups for matched parameters.

Parameters can be extracted from path expressions using a provided function:

import { extractRouteParametersFrom } from "@n7e/http-routing";

const parameters = extractRouteParametersFrom("some/path", pathExpression);

Optional parameters that are not present will be omitted from the parameters object. Here's a concrete example:

import { extractRouteParametersFrom, pathExpressionFor } from "@n7e/http-routing";

console.log(extractRouteParametersFrom("some/path", pathExpressionFor("/some/:parameter")));

// {parameter: "path"}

Decorators

If you prefer an object oriented approach there are some decorators available to easily create routable controllers. The decorators adds metadata to the controller class that can be retrieved with a provided function:

import { getDecoratorMetadataFor } from "@n7e/http-routing";

const metadata = getDecoratorMetadataFor(SomeController);

The metadata provided by the decorators has the following properties:

routePrefix
Controller wide base path.
routeMetadata
Controller wide metadata.
routableActions
Routable decorator actions mapped to controller methods.

Here's a simple example of a controller with a single routable decorator action:

import { Request, RequestMethod, Response } from "@n7e/http";
import { request } from "@n7e/http-routing";

class SomeController {
    @request(RequestMethod.GET, "some/path")
    public someMethod(request: Request): Promise<Response> {
        // ...
    }
}

You can use the @route() decorator to provide a controller wide path prefix like so:

import { Request, RequestMethod, Response } from "@n7e/http";
import { request, route } from "@n7e/http-routing";

@route("some/route")
class SomeController {
    @request(RequestMethod.GET, "some/path")
    public someMethod(request: Request): Promise<Response> {
        // ...
    }
}

Ultimately it's up to an implementation to add the registered decorator routes to a routing mechanism. But the idea is that the controller route prefix should prefix all route paths in the controller.

Request Method Decorators

For convenience there are dedicated decorators provided for common request methods. The provided decorators are:

  • CONNECT
  • DELETE
  • GET
  • HEAD
  • OPTIONS
  • PATCH
  • POST
  • PUT
  • TRACE

The functionality of these decorators are identical to that of @request() but the request method is omitted:

import { GET } from "@n7e/http-routing";
import type { Request, Response } from "@n7e/http";

class SomeController {
    @GET("some/path")
    public someMethod(request: Request): Promise<Response> {
        // ...
    }
}

Decorator Metadata

The optional last argument to all decorators allows us to provide arbitrary metadata to the routable decorator action.

import { Request, RequestMethod, Response } from "@n7e/http";
import { request, route } from "@n7e/http-routing";

@route("some/route", {key: "value"})
class SomeController {
    @request(RequestMethod.GET, "some/path", {key: "value"})
    public someMethod(request: Request): Promise<Response> {
        // ...
    }
}

The significance of this metadata is up to the implementations of the routing mechanism.

Routable Decorator Action

Routable decorator actions are almost identical to routable actions with two key differences:

pathPattern
Instead of a regular expression routable decorator actions uses the path pattern syntax to match requests against.
action
To enable creative freedom in how to structure the application routable decorator actions only have one restriction, and that's to return a promise resolving with an HTTP response. Whatever parameters (if any) the action accepts or requires is up to the implementor.

Readme

Keywords

Package Sidebar

Install

npm i @n7e/http-routing

Weekly Downloads

1

Version

0.1.0

License

MIT

Unpacked Size

47.1 kB

Total Files

35

Last publish

Collaborators

  • martin-n7e