Appearance
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:
Every request handler is invoked with
this
referring to some request context which is including reference on Hitchy's API in propertyapi
:javascriptfunction someRequestHandler( req, res ) { // access Hitchy's API via this.api here ... }
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 ... }
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
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
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
would populate this option with an object like this one:
javascript
{
_: [ "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 = { ... };
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
export default 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, .mjs or .cjs.
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
export const routes = { ... };
javascript
exports.routes = { ... };
or like this:
javascript
exports default function() {
return { routes: { ... } };
}
javascript
module.exports = function() {
return { routes: { ... } };
};
So, to be clear: The file's name does not 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.
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 is not 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
export default function( options, collected ) {
return { part: { hasInfo: collected.part.info != null } };
}
javascript
module.exports = function( options, collected ) {
return { part: { hasInfo: collected.part.info != null } };
};
This support has been added for api.config
can not 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 do not 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.
javascriptconfig.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.
javascriptconfig.routes = { "/some/route": [ "FooController.action", "FooController.altAction", ], };
javascriptconfig.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 dispatched to controllers.
Required
Routes are essential in Hitchy for it does not 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 is not 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"
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 }
as an arrow function,
javascript"/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
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" },
Note
controller and policy are just aliases for module supported for readability, only. They do not 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" ] },
Those arguments are appended to the regularly provided arguments
req
,res
and - in case of policies, only -next
:javascriptexport function action( req, res, foo, bar ) { // foo will contain "foo" due to given declaration // bar will contain "bar" due to given declaration }
javascriptexports.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
export const routes = {
"/some/route": "FooController.action",
"/api/:model": { module: "ModelController" },
"/api/:model/:id"( req, res ) {
res.end( "Hello World!" );
},
}
javascript
exports.routes = {
"/some/route": "FooController.action",
"/api/:model": { module: "ModelController" },
"/api/:model/:id"( req, res ) {
res.end( "Hello World!" );
},
}
Example With Routing Slots
javascript
export const routes = {
early: {
"/some/route": "FooController.action",
"/api/:model": { module: "ModelController" },
},
after: {
"/api/:model/:id"( req, res ) {
res.end( "Hello World!" );
},
}
}
javascript
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
} );
}
} );
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 does not 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 a 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 justsome/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" );
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:*,-*
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
it is available very early in Hitchy's bootstrap process and thus capable of customizing its early stages.
Meta information becomes available at the beginning of second stage discovering plugins. That's why it is already available in processing exposure stage preceding configuration stage since version 0.4.0.
it is meant to control more technical aspects of processing a plugin or the application without supporting different behaviour per installation of same plugin or application.
Controlling the order of plugin processing or the way of deriving component names from their implementing files' names is not meant to be customized per installation.
it is not polluting configuration object which is intended for more frequent use at runtime.
Application's meta information is read from two probable sources and merged into single set of data exposed here.
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 }
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 } }
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 deprecated
Using api.runtime
is deprecated since v0.7.0. It's elements have moved to
and their singular variants.
Accordingly, api.runtime.config
has moved to api.config
in v0.3.0.
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
api.utility.case
This subsection provides functions for converting between different practices for writing names.
kebab-case to PascalCase:
javascriptpascal = api.utility.case.kebabToPascal( "kebab-case" );
kebab-case to camelCase:
javascriptcamel = api.utility.case.kebabToCamel( "kebab-case" );
PascalCase to kebab-case:
javascriptkebab = api.utility.case.pascalToKebab( "PascalCase" );
camelCase to kebab-case:
javascriptkebab = api.utility.case.camelToKebab( "camelCase" );
camelCase to PascalCase:
javascriptpascal = 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:
javascripttarget = api.utility.object.merge( target, sourceA, sourceB, ... )
deeply sealing objects:
javascriptobject = 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:
javascriptobject = 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
and restarting the Hitchy application, your code can broadcast notifications to all connected clients like this:
javascript
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 );
} );
} );
Reserved namespace
socket.io supports namespaces e.g. for segmenting events. The namespace /hitchy
is reserved for Hitchy's core and official plugins.
Do not 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
}
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 have not been handled by any controller. You should not 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
Do not 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
}
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 is not 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 will not 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
On all requests lacking any body data null
is returned:
javascript
// if request does not 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
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"
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"
When providing patterns without forward slash like
png
orhtml
instead ofimage/png
ortext/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"
Some special cases differ from that first rule for being replaced with a different pattern internally.
provided actually tested text
text/plain
multipart
multipart/*
urlencoded
application/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"
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"
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"
}
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"
}
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 should not 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( { ... } );
However, signatures of methods natively provided as part of ServerResponse are not 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 do not 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
}
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" );
}
} )
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.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.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" } );
or
javascript
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" );
or
javascript
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.status( code )
Adjusts HTTP response status code.
Example
javascript
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 ) )
or
javascript
res.type( "image/png" ).send( bufferContainingPNG )