Skip to content

Hitchy's API

In this topic you learn how to access Hitchy's API in components of your application. It provides a detailed introduction of features exposed in that API.

Regarding request handling, there is an introduction of special context for every request handler and additional properties and methods provided as part of request and response descriptors provided as arguments there.

Event Support

Starting with v0.5.0 Hitchy's API is derived from EventEmitter and thus capable of emitting and dispatching events accordingly. This is used to improve Hitchy's integration with a web server or framework dispatching requests into Hitchy e.g. for supporting application shutting down itself.

Gaining Access

In a fully running application Hitchy's API is available

  • in every plugin complying with one of two patterns named common module pattern and common module function pattern,
  • in any function invoked on routing a request, that is
    • inline functions used in routing configurations,
    • methods of a controller and
    • methods of a policy.

Using Common Module Pattern

When implementing components you should comply with common module pattern to gain access on Hitchy's API.

In Request Handlers

In request handlers the API is exposed in two different ways:

  1. Every request handler is invoked with this referring to some request context which is including reference on Hitchy's API in property api:

    javascript
    function someRequestHandler( req, res ) {
        // access Hitchy's API via this.api here ...
    }
    function someRequestHandler( req, res ) {
        // access Hitchy's API via this.api here ...
    }
  2. Starting with v0.2.0 the API is exposed as property hitchy of given request descriptor as well. This enables use of arrow functions and helps with accessing the API in nested functions and callbacks more easily:

    javascript
    ( req, res ) => {
        // access Hitchy's API via req.hitchy here ...
    }
    ( req, res ) => {
        // access Hitchy's API via req.hitchy here ...
    }

Options

When talking about common module pattern before there was an argument passed providing so called Hitchy options.

Basically this is a set of parameters provided on invoking Hitchy. Usually this is done via Hitchy's CLI script. Thus, provided options are related to parameters provided on running CLI script. However, when testing Hitchy and its plugins it is possible to pass options from testing scripts as well.

bash
hitchy /app --pluginFolder /some/different/path --debug
hitchy /app --pluginFolder /some/different/path --debug

There are options recognized by Hitchy and some of those get qualified during bootstrap.

Custom Arguments support 0.6.2

Additional parameters are available in option arguments. That's why your application is implicitly capable of supporting custom CLI options for controlling its behaviour.

bash
hitchy /app --useAuthProvider https://some.auth.provider.com
hitchy /app --useAuthProvider https://some.auth.provider.com

This is a list of options recognized by Hitchy itself:

options.arguments 0.6.2

All CLI arguments are exposed in this option.

bash
hitchy start --port 3000 --saml --use-idp https://idp.example.com somefile.txt
hitchy start --port 3000 --saml --use-idp https://idp.example.com somefile.txt

would populate this option with an object like this one:

javascript
{
    _: [ "start", "somefile.txt" ],
    port: 3000,
    saml: true,
    "use-idp": "https://idp.example.com",
}
{
    _: [ "start", "somefile.txt" ],
    port: 3000,
    saml: true,
    "use-idp": "https://idp.example.com",
}

The syntax is due to using minimist for CLI parsing.

options.debug

This boolean option is controlling whether debug output is desired or not. Basically this option affects Hitchy to always enable any logging facility.

CLI Parameter

When using Hitchy's CLI the related parameter is called --debug.

options.dependencies

This option is supported to select plugins current application depends on. It is replacing any such list of plugins read from application's own hitchy.json file. In addition it is limiting set of eventually available plugins.

CLI Parameter

When using Hitchy's CLI the related parameter is called --depend-on. It can be provided multiple times.

options.explicitPlugins 0.4.0

This option is explicitly listing folders containing plugins and their local dependencies.

In opposition to more commonly useful option pluginsFolder this option is also checking the provided folder itself for implementing some plugin.

CLI Parameter

When using Hitchy's CLI the related parameter is called --plugin. It can be provided multiple times.

options.explicitPluginsOnly 0.4.0

This boolean option can be set to limit discovery of plugins to those folders listed explicitly using option explicitPlugins.

CLI Parameter

When using Hitchy's CLI the related parameter is called --explicit-only.

options.hitchyFolder

This option is defined by Hitchy during triangulation stage of bootstrap. It is addressing folder containing Hitchy installation used to manage currently running application.

CLI Parameter

There is no related CLI parameter for using Hitchy's CLI script already requires to pick its installation folder.

options.logger 0.8.0

This option works on embedding Hitchy in another framework or on testing a Hitchy-based application, only, as it requires complex data to be provided. Thus, there is no CLI argument affecting this option.

Hitchy has a pluggable logging manager that can be replaced by providing a custom manager here. The default manager integrates it with 3rd-party debug package. On unit-testing, the option can be set true to request injection of a capturing logger that is exposing all logged messages in an array for inspection rather than actually displaying them. In addition, a custom logger manager can be provided to implement any other way of processing log messages.

options.pluginsFolder

This option is picking different folder containing all available plugins in a node_modules sub-folder. The project folder is used in case of omitting this option.

CLI Parameter

When using Hitchy's CLI the related parameter is called --plugins.

options.projectFolder

This option is qualified during triangulation stage of bootstrap. It is addressing folder containing currently running application. When omitted current working directory is used by default.

CLI Parameter

When using Hitchy's CLI the related parameter is called --project.

Configuration

During bootstrap Hitchy is shallowly reading Javascript files available in sub-folder config of your application as well as of any plugin. Either file is assumed to export part of resulting configuration:

javascript
exports.part = { ... };
exports.part = { ... };

Either file may comply with common module pattern e.g. to gain access on Hitchy's options and its API or to provide its configuration asynchronously by returning Promise.

javascript
module.exports = function( options ) {
    const api = this;

    return checkSomeSource()
        .then( info => {
            return {
                part: { ... },
            };
        } );
};
module.exports = function( options ) {
    const api = this;

    return checkSomeSource()
        .then( info => {
            return {
                part: { ... },
            };
        } );
};

All files' exports are merged into one configuration object which is exposed at runtime as api.config in modules complying with common module pattern and as this.config in request handlers.

The structure of configuration depends on used plugins and it might include arbitrary information specific to your application, as well. However, some properties are discovered by Hitchy's core and thus they are listed below after some additional specifics on how configuration is basically compiled.

Rules For Naming Files

A configuration file's name

  • must not start with a full stop . and
  • must have extension .js.

Apart from that you may choose any name you like considering special file names supported as described below on order of processing configuration files.

As a convention, separate files should be used for every first-level property of resulting configuration object with the filename resembling that property's name. For example, configuration to be exposed as api.config.routes would be found in file config/routes.js and it is exporting related part like this:

javascript
exports.routes = { ... };
exports.routes = { ... };

or like this:

javascript
module.exports = function() {
	return { routes: { ... } };
};
module.exports = function() {
	return { routes: { ... } };
};

So, to be clear: The file's name doesn't affect the way exported configuration bits are merged. It is used to sort multiple configuration files. And it's meant to help you identify the file containing some particular slice of configuration you are interested in.

Order of Processing

Basically, configuration files may exist for every enabled plugin as well as the application itself. Hitchy is separately compiling configuration for every plugin first. After that, application's configuration is compiled accordingly and eventually all these separate configuration objects are merged into one just before enabling either plugin once again to adopt itself to that resulting configuration to be.

For every plugin and the application, either one's configuration files are sorted by name for sequential processing.

Individual Order Control

By optionally prefixing file names with integers you can control the order of processing individually. For example, renaming config/routes.js to config/90-routes.js makes sure it is processed after config/50-storage.js which would be succeeding without those prefixes.

In either context, special files are handled separately and you need to name them exactly as given here:

  • File config/local.js is meant to contain custom configuration provided by a user running your application.

    It isn't meant to be part of your application's distribution, but enables users to customize the configuration in a way safely surviving future upgrades. It will be processed after all regular configuration files sorted according to their name.

  • File config/final.js is another special file to be processed last per plugin or application.

    It is meant to eventually fix the configuration on behalf of either context right after reading whatever was found in a user's config/local.js file for individual customization.

    This feature has been added in version 0.6.10.

Accessing Collected Configuration 0.6.9

Configuration files complying with common module pattern may accept a second argument to exported function for accessing the configuration collected so far from files found in same folder:

javascript
module.exports = function( options, collected ) {
    return { part: { hasInfo: collected.part.info != null } };
};
module.exports = function( options, collected ) {
    return { part: { hasInfo: collected.part.info != null } };
};

This support has been added for api.config can't be used while processing configuration files, yet.

config.blueprints

Limitations

By intention, blueprints are supported in plugins, only.

Hitchy's Routing Concept

Reading the introduction on Hitchy's routing is highly recommended.

Declaration of blueprints is mostly equivalent to declaring routes in config.routes. In opposition to that blueprint declarations don't support separate lists per routing slot for there is only one slot for every plugin to declare its blueprint routes.

config.bodyParser

This property is exposing a function invoked with a buffer representing a request's raw body. It is invoked to parse this buffer for some contained information provided on invoking req.fetchBody() without any parameter. The function may return promise to deferredly deliver parsed content.

config.policies

Hitchy's Routing Concept

Reading the introduction on Hitchy's routing is highly recommended.

This property is exposing routing declarations of policies. Policies can be declared in plugins as well as in application.

The supported format is mostly identical to the one supported for config.routes below.

Related topic

See config.routes for additional information.

These are the differences between policies and routes:

  • When declaring policies targets are addressing policy components instead of controller components.

  • Targets of a route may be declared with optional suffix Controller. When declaring policy targets the supported suffix is Policy instead.

    javascript
    config.routes = {
        "/some/route": "FooController.action",
    };
    config.policies = {
        "/some/route": "FooPolicy.action",
    };
    config.routes = {
        "/some/route": "FooController.action",
    };
    config.policies = {
        "/some/route": "FooPolicy.action",
    };
  • When declaring routes every source must be linked with exactly one target. When declaring policies every source may also have a list of targets to be processed in order.

    bad:

    javascript
    config.routes = {
        "/some/route": [
            "FooController.action",
            "FooController.altAction",
        ],
    };
    config.routes = {
        "/some/route": [
            "FooController.action",
            "FooController.altAction",
        ],
    };

    good:

    javascript
    config.routes = {
        "/some/route": "FooController.action",
    };
    config.policies = {
        "/some/route": [
            "FooPolicy.action",
            "FooPolicy.altAction",
        ],
    };
    config.routes = {
        "/some/route": "FooController.action",
    };
    config.policies = {
        "/some/route": [
            "FooPolicy.action",
            "FooPolicy.altAction",
        ],
    };

config.routes

Hitchy's Routing Concept

Reading the introduction on Hitchy's routing is highly recommended.

This property is exposing declarations of (terminal) routes.

Required

Routes are essential in Hitchy for it doesn't know how to handle incoming requests otherwise.

A set of routing declarations is given as Object or as a Map with the latter assuring certain order of processing, though this isn't quite as important in most cases due to Hitchy's routing preferring to match longer sources over shorter ones.

The set may be divided into slots for providing different sets per routing slot. Supported names for dividing declarations are early, before, after and late. By default, routing declarations are applied to before slot.

Limitations

Providing declarations per routing slot is not supported for plugins.

Every single declaration is associating a routing source with a routing target.

  • The source is a string optionally selecting an HTTP method and mandatorily providing a pattern to be matched by a request's URL path. Patterns are supported using path-to-regexp.

    Here are some valid examples:

    "/some/path"
    "GET /api/:model/:id"
    "POST /api/:model/write/:id"
    "ALL /api/:model"
    "/some/path"
    "GET /api/:model/:id"
    "POST /api/:model/write/:id"
    "ALL /api/:model"

    When omitting provision of HTTP method it defaults to GET.

  • The target is given

    • as a regular function,

      javascript
      "/some/route": function( req, res ) {
          // TODO implement this handler
      }
      "/some/route": function( req, res ) {
          // TODO implement this handler
      }
    • as an arrow function,

      javascript
      "/some/route": ( req, res )  => {
          // TODO implement this handler
      }
      "/some/route": ( req, res )  => {
          // TODO implement this handler
      }
    • as a string describing function exposed by available controller component or

      javascript
      "/some/route": "FooController.action",
      "/some/route": "Foo.action", // equivalent for "Controller" is optional
      "/some/route": "Foo::action", // equivalent, just in case you prefer this notation style
      "/some/route": "FooController.action",
      "/some/route": "Foo.action", // equivalent for "Controller" is optional
      "/some/route": "Foo::action", // equivalent, just in case you prefer this notation style
    • as an object selecting function exposed by available controller component.

      javascript
      "/some/route": { module: "FooController", method: "action" },
      "/some/route": { module: "Foo", method: "action" },
      "/some/route": { module: "Foo" }, // default for "method" is "index"
      "/some/route": { controller: "Foo" },
      "/some/route": { policy: "Foo" },
      "/some/route": { module: "FooController", method: "action" },
      "/some/route": { module: "Foo", method: "action" },
      "/some/route": { module: "Foo" }, // default for "method" is "index"
      "/some/route": { controller: "Foo" },
      "/some/route": { policy: "Foo" },

      Note

      controller and policy are just aliases for module supported for readability, only. They don't enable use of controllers in declaring policies or use of policies in declaring routes.

      Using this most complex syntax it is possible to declare additional arguments provided on invoking either request handler:

      javascript
      "/some/route": { module: "Foo", method: "action", args: [
          "foo",
          "bar"
      ] },
      "/some/route": { module: "Foo", method: "action", args: [
          "foo",
          "bar"
      ] },

      Those arguments are appended to the regularly provided arguments req, res and - in case of policies, only - next:

      javascript
      // in api/controllers/foo.js
      exports.action = function( req, res, foo, bar ) {
          // foo will contain "foo" due to given declaration
          // bar will contain "bar" due to given declaration
      }
      // in api/controllers/foo.js
      exports.action = function( req, res, foo, bar ) {
          // foo will contain "foo" due to given declaration
          // bar will contain "bar" due to given declaration
      }

Regular Example

javascript
exports.routes = {
    "/some/route": "FooController.action",
    "/api/:model": { module: "ModelController" },
    "/api/:model/:id"( req, res ) {
        res.end( "Hello World!" );
    },
}
exports.routes = {
    "/some/route": "FooController.action",
    "/api/:model": { module: "ModelController" },
    "/api/:model/:id"( req, res ) {
        res.end( "Hello World!" );
    },
}

Example With Routing Slots

javascript
exports.routes = {
    early: {
        "/some/route": "FooController.action",
        "/api/:model": { module: "ModelController" },
    },
    after: {
        "/api/:model/:id"( req, res ) {
            res.end( "Hello World!" );
        },
    }
}
exports.routes = {
    early: {
        "/some/route": "FooController.action",
        "/api/:model": { module: "ModelController" },
    },
    after: {
        "/api/:model/:id"( req, res ) {
            res.end( "Hello World!" );
        },
    }
}

API Elements

Hitchy's API can be divided into several sections to be described here.

Note

The following description assumes you know how to gain access on Hitchy's API in either situation, thus referring to it using just api.

api.Client

This class is providing router client suitable for locally triggering request dispatching.

Obey The Naming!

This class is exposed using PascalCase name to comply with common rules of code style linters.

This client can be used to simulate requests for testing purposes, for improved request processing e.g. via websockets or for locally triggering request handlers e.g. for transparently rewriting requests or similar.

Example

javascript
const client = new api.Client( {
    method: "POST", 
    url: "/some/internal/url?foo=bar",
    headers: {
        "content-type": "text/json",
    },
} );

client.end( JSON.stringify( { some: "input" } ) );

return client.dispatch()
    .then( res => {
        if ( res.statusCode === 200 ) {
            return res.body()
                .then( body => JSON.parse( body.toString( "utf8" ) ) )
                .then( data => {
                    // TODO: process response
                } );
        }
    } );
const client = new api.Client( {
    method: "POST", 
    url: "/some/internal/url?foo=bar",
    headers: {
        "content-type": "text/json",
    },
} );

client.end( JSON.stringify( { some: "input" } ) );

return client.dispatch()
    .then( res => {
        if ( res.statusCode === 200 ) {
            return res.body()
                .then( body => JSON.parse( body.toString( "utf8" ) ) )
                .then( data => {
                    // TODO: process response
                } );
        }
    } );

Note

This client simulates most parts of ClientRequest and ServerResponse. In addition method res.body() is provided to simplify retrieval of response body.

api.cmfp

Signature: api.cmfp( someFunction, [ arg1, arg2 ] )

This method is invoking some function provided by reference with support for common module function pattern. This pattern is derived from common module pattern and is meant to invoke particular functions of plugins in a similar way.

Invoked functions are assumed to expect Hitchy's API as this and global options provided in first argument which is implicitly prepended by this method.

api.cmp

Signature: api.cmp( "./my/module", [ arg1, arg2 ] )

This method is loading selected module with support for common module pattern. Optionally provided arguments are passed in addition to options on invoking function exposed by selected module as part of common module pattern.

If selected module doesn't comply with that pattern it is loaded as usual.

api.config 0.3.0

All configuration of every available plugin as well as the application itself is loaded from Javascript files in either ones' config sub-folder and merged into a single configuration object which is exposed here.

api.config.$appConfig

Configuration provided in api.config always includes options exported by available plugins. If you need to access the application's own configuration - which is merged from reading all Javascript files in application's sub-folder config - this basically hidden property can be used in either plugin's configure() method.

This is the application's counterpart to either plugin's exposure of its pure configuration.

api.controllers 0.7.0

Exposes all controller components available in current application. api.controller is available for accessing the same collection, too.

api.crash() 0.5.0

Signature: api.crash( Error ) : Promise

Using this function an application may crash itself by intention. The provided error is logged before shutting down application and its request listener.

This function may be useful when running a Hitchy application in a container. Consider your application has entered some failed state it might want to recover from the easy way by simply restarting on purpose. This is possible by invoking this function.

This function is different from api.shutdown() by writing provided error to log file and exit process with non-zero status code eventually. The returned promise is always rejected with the provided error. In addition, when called before promise returned from api.shutdown() is settled, that promise will be rejected as well.

api.data

Hitchy's API gets sealed after bootstrap has finished to prevent intended or accidental change of any exposed API.

This property is excluded in particular to provide a common space multiple components can use for saving and sharing data that's exceeding the lifetime of a single request.

api.folder()

This function takes a relative path name of a file or folder and qualifies it in context of current hitchy-based project, which is your application.

The provided relative path name may start with special markers to select one of several supported base folders:

  • @project/some/file.ext is selecting some/file.ext in context of your current project's folder. This is the default behaviour when providing just some/file.ext, too.
  • @hitchy/some/file.ext is selecting some/file.ext in context of folder containing currently used core implementation of Hitchy. Usually, this folder is located in node_modules/@hitchy/core of your project's folder, but using command line arguments it is possible to pick a different location.

api.log()

This function is a generator for logging facilities. It is invoked with the name of a logging facility and returns another function which can be used to actually generate log messages on behalf of either facility.

Example

javascript
const logAlert = api.log( "hitchy:plugin:tooling:alert" );
const logDebug = api.log( "hitchy:plugin:tooling:debug" );

logDebug( "got some configuration" );

logAlert( "connection lost" );
const logAlert = api.log( "hitchy:plugin:tooling:alert" );
const logDebug = api.log( "hitchy:plugin:tooling:debug" );

logDebug( "got some configuration" );

logAlert( "connection lost" );

Basically, facilities are supported to cluster your application's log and help with controlling what messages are actually logged or not making it more useful in either situation.

Environment-Based Control

Environment variable DEBUG is examined for providing a comma-separated list of facilities to be enabled.

bash
DEBUG=hitchy:*,myapp:*,-*
DEBUG=hitchy:*,myapp:*,-*

Every item of that list

  • is a facility name to be logged,
  • may be prefixed with single dash - to explicitly exclude a matching facility from logging,
  • may contain asterisk * for matching multiple facilities to be enabled or disabled.

Naming Convention

Facility names should represent a hierarchy of facilities by extending the name of a superordinated facility with a colon and the intended facility's name.

Sticking to this pattern is beneficial on using asterisk * in logging control.

api.meta 0.4.0

This property is exposing application's meta information which is similar meta information attached to every plugin. In both cases meta information can be considered another set of configuration. It can be distinguished, though, for

Application's meta information is read from two probable sources and merged into single set of data exposed here.

  1. An application may have its own hitchy.json file in its root folder.

    Example

    Assume to have a file named hitchy.json with following content:

    json
    {
        "appendFolders": false
    }
    {
        "appendFolders": false
    }
  2. In addition its package.json file may provide meta information in special property hitchy.

    Example

    The same information given in example above could be provided via application's package.json file like this:

    json
    {
        "name": "my-app",
        "version": "1.0.0",
        ...
        "hitchy": {
            "appendFolders": false
        }
    }
    {
        "name": "my-app",
        "version": "1.0.0",
        ...
        "hitchy": {
            "appendFolders": false
        }
    }

Data found in hitchy.json file is preferred over data found in package.json. Most meta information elements supported for plugins are ignored in context of application except for these:

api.models 0.7.0

Exposes all model components available in current application. api.model is available for accessing the same collection, too.

api.plugins

All discovered and loaded plugins are listed in this property. It is an object mapping a plugin's role (which might be different from its name!) into either plugin's API.

api.policies 0.7.0

Exposes all policy components available in current application. api.policy is available for accessing the same collection, too.

api.runtime

Planned obsolescence

Starting v0.7.0, all four collections described below and their singular aliases have become first-class properties of Hitchy's API, too. In favor of those, api.runtime is going to be removed in a future version.

This section of Hitchy's API is exposing a compilation of all components exposed by discovered plugins as well as current application itself. They are grouped by component type.

  • Controllers are exposed in api.runtime.controllers.
  • Policies are exposed in api.runtime.policies.
  • Models are exposed in api.runtime.models.
  • Services are exposed in api.runtime.services.

For the sake of flexibility and fault tolerance either group is exposed using its singular name as well. Thus using api.runtime.controller is equivalent to using api.runtime.controllers etc.

As of v0.7.0, additional aliases have been introduced as api.controllers, api.policies, api.models and api.services. This includes singular variants, too.

Change of API

Starting with version 0.3.0 the configuration isn't available as api.runtime.config anymore, but exposed as api.config.

api.services 0.7.0

Exposes all service components available in current application. api.service is available for accessing the same collection, too.

api.shutdown() 0.5.0

Signature: api.shutdown() : Promise

Application may intentionally shut down itself by invoking this function. It is returning a promise which is controlled by events emitted on API. This promise is

  • fulfilled on next close event and
  • rejected on next error event.

api.utility.case

This subsection provides functions for converting between different practices for writing names.

  • kebab-case to PascalCase:

    javascript
    pascal = api.utility.case.kebabToPascal( "kebab-case" );
    pascal = api.utility.case.kebabToPascal( "kebab-case" );
  • kebab-case to camelCase:

    javascript
    camel = api.utility.case.kebabToCamel( "kebab-case" );
    camel = api.utility.case.kebabToCamel( "kebab-case" );
  • PascalCase to kebab-case:

    javascript
    kebab = api.utility.case.pascalToKebab( "PascalCase" );
    kebab = api.utility.case.pascalToKebab( "PascalCase" );
  • camelCase to kebab-case:

    javascript
    kebab = api.utility.case.camelToKebab( "camelCase" );
    kebab = api.utility.case.camelToKebab( "camelCase" );
  • camelCase to PascalCase:

    javascript
    pascal = api.utility.case.camelToPascal( "camelCase" );
    pascal = api.utility.case.camelToPascal( "camelCase" );

api.utility.file

This subsection provides helper functions used by Hitchy for accessing local file system.

Deprecation Warning

There is a more powerful package file-essentials and we consider to replace these functions with that package in a future release.

api.utility.object

This subsection provides helper functions for

  • deeply merging objects:

    javascript
    target = api.utility.object.merge( target, sourceA, sourceB, ... )
    target = api.utility.object.merge( target, sourceA, sourceB, ... )
  • deeply sealing objects:

    javascript
    object = api.utility.object.seal( object )
    object = api.utility.object.seal( object )

    A callback may be provided in second argument to be called on every object to be sealed. It is invoked with breadcrumb of property names as array of strings and assumed to return boolean indicating whether either object should be actually sealed or not.

  • deeply freezing objects:

    javascript
    object = api.utility.object.freeze( object )
    object = api.utility.object.freeze( object )

    A callback may be provided in second argument to be called on every object to be frozen. It is invoked with breadcrumb of property names as array of strings and assumed to return boolean indicating whether either object should be actually frozen or not.

api.utility.promise

This subsection is passing API of module promise-essentials Hitchy is relying on to manage more complex asynchronous processes. It is exposed here for use in plugins and applications to prevent multiple dependencies on that module in a single resulting application.

api.websocket 0.7.0

This property is exposing the websocket server instance integrated with Hitchy if related plugin is installed. Currently, the plugin @hitchy/plugin-socket.io is available to integrate socket.io with Hitchy.

Examples

After installing a plugin e.g. with

bash
npm i @hitchy/plugin-socket.io
npm i @hitchy/plugin-socket.io

and restarting the Hitchy application, your code can broadcast notifications to all connected clients like this:

javascript
api.websocket.emit( "my-info", someData );
api.websocket.emit( "my-info", someData );

It can set up listeners for incoming notifications, too:

javascript
api.websocket.listen( "connection", socket => {
	socket.on( "client-info", ( arg1, arg2 ) => {
		// TODO handle the request, maybe respond with another message in return
        
        socket.emit( "client-response", result1, result2 );
    } );
} );
api.websocket.listen( "connection", socket => {
	socket.on( "client-info", ( arg1, arg2 ) => {
		// TODO handle the request, maybe respond with another message in return
        
        socket.emit( "client-response", result1, result2 );
    } );
} );

Reserved namespace

socket.io supports namespaces e.g. for segmenting events. The namespace /hitchy is reserved for Hitchy's core and official plugins.

Don't use the namespace /hitchy for anything but interacting with Hitchy's official features.

Request Context

On request handling routes are used to select handlers to be invoked. Those handlers are functions invoked with this referring to a request context which is providing information related to currently dispatched request.

Example

In a request handler like

javascript
function( req, res ) {
    // TODO add some handling code here
}
function( req, res ) {
    // TODO add some handling code here
}

the request context is available using this.

this.api

Hitchy's API is provided in request context for simplified access.

this.config 0.3.0

This property is an alias for simplified access on api.config of Hitchy's API exposing current runtime configuration.

this.consumed

This property contains markers used internally to handle cases that haven't been handled by any controller. You shouldn't use or adjust those marks.

this.context 0.3.0

This property is a string naming the service hitchy is integrated with. Currently supported values are:

  • standalone when running current application without any integration into some other application.
  • express when running current application with Hitchy integrated into an Express-based application.

this.controllers 0.3.0

This is another alias for simplifying access on collection of available controllers.

Alias

Using this.controller is supported as well.

this.done()

This callback is provided by the service Hitchy is integrating with. When using Hitchy with Express invoking this function is starting next handler function registered with Express skipping any code that's authoritative in scope of Hitchy integrating with Express.

Caution

Don't use this function for it might be causing significant side effects.

this.local

This object is provided for sharing volatile information between handlers involved in handling a particular request. This information is shared between policies and controllers participating in handling a request and gets dropped when handling request has finished.

this.models 0.3.0

This alias is simplifying access on collection of available models.

Alias

Using this.model is supported as well.

this.policies 0.3.0

This alias is simplifying access on collection of available policies.

Alias

Using this.policy is supported as well.

this.request

This property is a reference on IncomingMessage describing current request to be handled. It is identical to the reference provided in first argument usually named req of either handler.

this.response

This property is a reference on ServerResponse for managing response to be sent. It is identical to the reference provided in second argument usually named res of either handler.

this.runtime

This property is an alias for simplified access on api.runtime of Hitchy's API exposing available components.

this.services 0.3.0

This alias is simplifying access on collection of available services.

Alias

Using this.service is supported as well.

this.startTime

This property is exposing the time of handling current request has started as number of milliseconds since midnight of January 1st, 1970.

this.url 0.6.16

The current request's full URL is exposed here. It considers optionally available Forwarded header when compiling the URL.

this.clientIp 0.6.17

Provides IP address of current request's peer taking additional information injected by any involved reverse proxy into account.

Request Helpers

In request handlers of controllers and policies there are two provided arguments usually named req and res. The former is basically an IncomingMessage and the latter is a ServerResponse. But either object is extended to provide additional information and functionality.

Standalone vs. Integrated With ExpressJS

Most helpers described here are basically available in a standalone Hitchy application. When using Hitchy integrated with ExpressJS most of these helpers are provided by ExpressJS and thus may behave differently.

Example

In a request handler like

javascript
function( req, res ) {
    // TODO add some handling code here
}
function( req, res ) {
    // TODO add some handling code here
}

the request helpers are available as part of object provided as req.

req.accept

A properly sorted list of MIME type ranges is provided in this property according to any current request's Accept header field. There is always a list of MIME ranges and either item is provided without optional parameters.

Example

When handling request with header Accept: text/*;q=0.5, text/json this property will expose sorted list of given MIME ranges [ "text/json", "text/*" ].

req.api 0.5.3

This property is virtually aliasing req.hitchy to support framework-agnostic code.

req.context

This property is exposing the request context which is also available via this in an invoked request handler. However, due to binding to different contexts use of this might not be an option.

req.cookies

Requires Plugin

This property requires installation of plugin @hitchy/plugin-cookies as a dependency of your project.

This property exposes object containing all cookies transmitted by client in current request.

req.fetchBody( parser )

This method promises request's body. The optional parameter can be used to control parser used for extracting contained information.

Warning

When integrating with ExpressJS it might have fetched and parsed request body before exposing it as req.body. In this case Hitchy isn't capable of accessing raw request body anymore. This is limiting use of this function.

Supporting this scenario any existing data in req.body will be delivered whenever using this function for fetching request's body without regards to some optionally selected parser.

  • When omitted or set null any parser function in configuration is used to commonly parse raw body for contained information. When there is no configured parser some fallback is used supporting JSON and form-encoded request bodies.

  • On providing function here this one is used to parse the raw body instead of any configured or fallback parser.

  • On providing false the raw body is promised as instance of Buffer. This is bypassing any parser to be invoked thus won't result in caching some parser's output as well.

Different Parsers and Body Caching

This method is caching any previously extracted body data in association with provided parser argument. Thus, re-invoking this method with a different parser results in parsing raw body again while providing same parser re-fetches same information as before.

When providing custom function make sure to provide the same instance of that function to benefit from this caching. As an option assign a global body parser function in configuration as config.bodyParser.

req.hitchy 0.2.0

This property is exposing Hitchy's API. It has been named hitchy instead of a more common api to prevent it from interfering with other framework's API in portable code meant to run with different frameworks.

Virtual Alias 0.5.3

There is a virtual alias additionally exposing Hitchy's API as req.api unless this property exists in request descriptor. This is useful for writing code agnostic to supported frameworks.

req.ignore() 0.7.3

Instructs the current request's dispatcher to stop invoking further matching request handlers based on configuration of policies and routes. This can be used to prevent Hitchy from interfering with external request handlers.

WARNING

The current request handler which is invoking req.ignore() must finish as intended. E.g., a policy handler has to invoke its next callback.

req.is() 0.5.3

When discovering format of some provided request body data this function may be used to conveniently check the client-provided information in request header content-type.

The function takes one or more patterns matching expected formats and the first one matching current request will be returned. If neither listed pattern is matching, false is returned.

javascript
// if request claims to provide application/json
req.is( "application/json" ); // --> "application/json"
req.is( "json" );             // --> "json"
req.is( "*/json" );           // --> "*/json"
req.is( "json", "*/json" );   // --> "*/json"
req.is( "text", "json" );     // --> "json"
req.is( "text" );             // --> false
// if request claims to provide application/json
req.is( "application/json" ); // --> "application/json"
req.is( "json" );             // --> "json"
req.is( "*/json" );           // --> "*/json"
req.is( "json", "*/json" );   // --> "*/json"
req.is( "text", "json" );     // --> "json"
req.is( "text" );             // --> false

On all requests lacking any body data null is returned:

javascript
// if request doesn't seem to provide any octet of body data
req.is( "application/json" ); // --> null
req.is( "json" );             // --> null
req.is( "*/json" );           // --> null
req.is( "json", "*/json" );   // --> null
req.is( "text", "json" );     // --> null
req.is( "text" );             // --> null
// if request doesn't seem to provide any octet of body data
req.is( "application/json" ); // --> null
req.is( "json" );             // --> null
req.is( "*/json" );           // --> null
req.is( "json", "*/json" );   // --> null
req.is( "text", "json" );     // --> null
req.is( "text" );             // --> null

If there is some body data but request header content-type is missing, this function is returning false:

javascript
// there is body data, but "content-type" is missing
req.is( "application/json" ); // --> false
req.is( "json" );             // --> false
req.is( "*/json" );           // --> false
req.is( "json", "*/json" );   // --> false
req.is( "text", "json" );     // --> false
req.is( "text" );             // --> false
// there is body data, but "content-type" is missing
req.is( "application/json" ); // --> false
req.is( "json" );             // --> false
req.is( "*/json" );           // --> false
req.is( "json", "*/json" );   // --> false
req.is( "text", "json" );     // --> false
req.is( "text" );             // --> false

Supported test patterns are mostly equivalent to the ones supported by type-is which is used in express. However, this implementation might differ from that one in some aspects:

  • All string-based tests work case-insensitively.

    javascript
    // if request header contains `content-type` with `AppliCatIon/JsON`
    req.is( "json" );                           // --> "json"
    req.is( "application/json" );               // --> "application/json"
    // if request header contains `content-type` with `AppliCatIon/JsON`
    req.is( "json" );                           // --> "json"
    req.is( "application/json" );               // --> "application/json"

    On matching, the provided pattern is returned as-is, though.

    javascript
    // if request header contains `content-type` with `AppliCatIon/JsON`
    req.is( "JSON" );                           // --> "JSON"
    req.is( "aPPLicATion/JSOn" );               // --> "aPPLicATion/JSOn"
    // if request header contains `content-type` with `AppliCatIon/JsON`
    req.is( "JSON" );                           // --> "JSON"
    req.is( "aPPLicATion/JSOn" );               // --> "aPPLicATion/JSOn"
  • When providing patterns without forward slash like png or html instead of image/png or text/html this library simply requires that pattern to match either first or second part of found MIME information.

    javascript
    // if request header contains `content-type` with `text/html`
    req.is( "html" );                           // --> "html"
    // if request header contains `content-type` with `image/png`
    req.is( "image" );                          // --> "image"
    req.is( "png" );                            // --> "png"
    // if request header contains `content-type` with `text/html`
    req.is( "html" );                           // --> "html"
    // if request header contains `content-type` with `image/png`
    req.is( "image" );                          // --> "image"
    req.is( "png" );                            // --> "png"
  • Some special cases differ from that first rule for being replaced with a different pattern internally.

    providedactually tested
    texttext/plain
    multipartmultipart/*
    urlencodedapplication/x-www-urlencoded
    +json*/*+json
    +xml*/*+xml

    Just like in type-is the last qualification examples apply to any provided string starting with +.

  • All patterns can contain * for basically matching any number of characters. It may appear multiple times and in any position of your pattern. However, neither case is matching the forward slash for being applied on separated halves of MIME information.

    javascript
    // if request header contains `content-type` with `text/html`
    req.is( "text", "te*tml", "t*e*x*t" );      // --> "t*e*x*t"
    // if request header contains `content-type` with `text/html`
    req.is( "text", "te*tml", "t*e*x*t" );      // --> "t*e*x*t"
  • You may provide regular expressions instead of strings. Those are applied to the full content of content-type header. On match the actual MIME information found in request header without optional qualifiers such as ;charset=UTF-8 is returned.

    javascript
    // if request header contains `content-type` with `application/json; charset=UTF-8`
    req.is( /.*\/json\b/ );      // --> "application/json"
    // if request header contains `content-type` with `application/json; charset=UTF-8`
    req.is( /.*\/json\b/ );      // --> "application/json"

req.params

This object is populated with named segments of currently dispatched route.

Example

If your handler is bound to handle a route like GET /api/:model/:item and client was requesting /api/user/123 then req.params looks like this:

json
{
    model: "user",
    item: "123"
}
{
    model: "user",
    item: "123"
}

req.path

This property conveniently provides current request's path which is the requested URL without any appended query string or hash.

Example

On requesting /some/path/name?with=arg this property will provide /some/path/name.

req.query

This property is an object exposing all query parameters.

Example

On requesting /some/path/name?with=arg&another=one this property will provide the following object:

json
{
    with: "arg",
    another:  "one"
}
{
    with: "arg",
    another:  "one"
}

req.res

This property is exposing the response manager related to current request. It is provided for compatibility with API of Express.

req.session

Requires Plugin

This property requires installation of plugin @hitchy/plugin-session as a dependency of your project.

This object consists of two properties meant to provide server-side session shared by different requests transmitted from same client.

  • In req.session.user currently authenticated user is stored.

    WARNING

    This feature needs installation of another plugin, e.g. @hitchy/plugin-auth.

  • req.session.data can be used in your controllers and policies to store arbitrary information regarding currently requesting client.

    Make sure any stored information can be serialized, thus you shouldn't put instances of some custom class in here, but use native data suitable for converting to/from JSON, only.

Supported Scenarios

Sessions rely on client passing session cookie in every request following some initial one. Usually this is available with browsers requesting pages and assets on behalf of a user. You should not rely on this session feature when it comes to REST APIs, though.

Response Helpers

In handling requests there is a response manager provided in second argument usually named res. Basically this is an instance of ServerResponse. But Hitchy is injecting some additional methods for simplifying generation of responses.

Fluent Interface

Those additional methods listed below are providing fluent interface for chaining multiple invocations.

javascript
res.status( 400 ).set( "content-type", "text/json" ).send( { ... } );
res.status( 400 ).set( "content-type", "text/json" ).send( { ... } );

However, signatures of methods natively provided as part of ServerResponse aren't adjusted.

Preventing Response on HEAD Requests 0.2.2

Requests using HTTP method HEAD must not provide a response. Disobeying this usually results in exceptions thrown e.g. on trying to send some JSON-formatted response.

Hitchy is designed to detect any such request limiting capabilities of response manager's methods related to describing some actual content. That's why you don't need to take care of omitting response content in handlers supported HEAD requests as well.

Standalone vs. Integrated With ExpressJS

Most helpers described here are basically available in a standalone Hitchy application. When using Hitchy integrated with ExpressJS most of these helpers are provided by ExpressJS and thus may behave differently.

Example

In a request handler like

javascript
function( req, res ) {
    // TODO add some handling code here
}
function( req, res ) {
    // TODO add some handling code here
}

the response helpers are available as part of object provided as res.

res.format( handlers )

This method provides different handlers for generating response with each handler bound to one particular type or format of response data. According to current request's Accept header the best matching handler is picked to eventually create a response.

A special handler named default is used if neither provided handler is matching any type of response accepted in current request. If this default handler is missing a 406 Not Acceptable is issued implicitly in that case.

Handlers' names are either MIME types or some supported filename extensions considered aliases for related MIME type.

Example

Using this function in a request handler

javascript
res.format( {
    html( req, res ) {
        res.send( "<html>...</html>" );
    },
    "text/json"( req, res ) {
        res.json( { some: "data" } );
    },
    default( req, res ) {
        res.status(400).send( "unsupported type of response" );
    }
} )
res.format( {
    html( req, res ) {
        res.send( "<html>...</html>" );
    },
    "text/json"( req, res ) {
        res.json( { some: "data" } );
    },
    default( req, res ) {
        res.status(400).send( "unsupported type of response" );
    }
} )

the response is provided as JSON when requesting with Accept: text/json and as an HTML document when requesting with Accept: text/html.

res.json( data )

This method generates a JSON-formatted response and sends it to the client. It is ending current response implicitly.

Example

javascript
res.json( { some: "data" } );
res.json( { some: "data" } );

res.redirect( statusCode, url )

Use this helper to instantly generate and send response requesting user to fetch different URL for some desired information.

Example

javascript
res.redirect( 301, "https://example.com/" );
res.redirect( 301, "https://example.com/" );

res.send( content )

This method is sending provided content to the client implicitly ending response to current request. The response's type of content depends on type of value provided as content here:

  • Providing an object the response is JSON-formatted.

  • When providing a string the response is sent as plain text unless having set some different content-type header before.

  • When providing an instance of Buffer or any other kind of data the response is an octet stream with MIME type application/octet-stream.

Examples

javascript
res.send( { some: "data" } );
res.send( { some: "data" } );

or

javascript
res.send( Buffer.from( "..." ) );
res.send( Buffer.from( "..." ) );

res.set( name, value )

Adjusts single field of response header. Internally this function is invoking res.setHeader().

Examples

javascript
res.set( "content-type", "text/json;charset=utf8" );
res.set( "content-type", "text/json;charset=utf8" );

or

javascript
res.set( "x-api-level", "3" );
res.set( "x-api-level", "3" );

res.set( fields )

Adjusts multiple fields of response header at once. Internally this function is invoking res.setHeader() for every listed header field.

Example

javascript
res.set( {
    "content-type": "text/json;charset=utf8",
    "x-api-level": "3"
} );
res.set( {
    "content-type": "text/json;charset=utf8",
    "x-api-level": "3"
} );

res.status( code )

Adjusts HTTP response status code.

Example

javascript
res.status( 404 ).json( { error: "no such data" } )
res.status( 404 ).json( { error: "no such data" } )

res.type( mime )

Adjusts content-type header field of response supporting several aliases for simplified selection of response type.

Examples

javascript
res.type( "json" ).send( JSON.stringify( true ) )
res.type( "json" ).send( JSON.stringify( true ) )

or

javascript
res.type( "image/png" ).send( bufferContainingPNG )
res.type( "image/png" ).send( bufferContainingPNG )