Components

A Hitchy-based application relies on components to be exposed by the application as well as any available plugin. During bootstrap Hitchy is searching for Javascript files implementing components in certain folders of every plugin as well as the application.

Those folders are mentioned below in context of either type of component.

When loading a component's file it is assumed to export the component's API. This might be some class or some object providing its methods.

Every component is exposed at runtime using a name for either component which is derived from its file's relative path name. When multiple files in installed plugins and your application result in same name of component only the last one read will be exposed. However either file is loaded with a reference to the existing component it is replacing.

Kinds of Components

Hitchy knows four different kinds of components described below. Either kind of component is assumed to be implemented as a CommonJS module as supported by Node.js currently.

Controllers

A controller is a module or class that is exposing methods for eventually handling requests and sending some response. Controllers are discovered in folder api/controllers of your application as well as any installed plugin.

config/routes.js

exports.routes = {
    "/my/route": "GreetingsController.sayHey"
};

api/controllers/greetings.js

module.exports = {
    sayHey( req, res ) {
        res.send( "Hey!" );
    }
};

About Discovery

Talking about the discovery of controllers doesn't imply to have some routes defined for linking either controller's handlers with requests they are meant to handle. Any routing configuration as provided in first example above must be defined manually.

Policies

A policy is a module or class just like a controller. However, it is meant to handle requests collaboratively with other policies and some final controller. Thus it might ignore requests or adjust request information without ever responding to any request. Multiple policies may be involved in processing a single request. That's why policies are capable of passing control to next available policy in chain of processing policies which isn't possible for a controller.

Policies are always able to send a response nonetheless. That's useful for implementing filters next to controllers.

A final difference between controllers and policies regards the way they are picked to be involved in processing a particular request. Policies are applied to requests sharing prefix of URL path. A controller is obeyed when fully matching a request's URL path.

Example

Consider a client is requesting URL /api/user/search?name=John.

This request would pick controller routes matching /api/user/search as a whole, only.

In opposition to that, any policy route matching /, /api, /api/user or /api/user/search would be picked up for processing in order from shortest to longest matching prefix.

Policy components are discovered in folder api/policies of your application as well as any installed plugin.

config/routes.js

exports.policies = {
    "/": "BodyPolicy.accessGranted"
};

api/policies/body.js

module.exports = {
    accessGranted( req, res, next ) {
        if ( req.query.token === "secret" ) {
            req.accessGranted = true;
            res.set( "x-granted", "1" );
            next();
        } else {
            res.status( 403 ).json( { error: "access forbidden" } );
        }
    }
};

Remarks

The preceding example illustrates support for callback provided in third argument for calling next policy in chain of policies to be applied. In fact, any policy hast to invoke this callback unless it is returning a promise instead.

The example also illustrates a policy adjusting response without responding by setting response header.

Eventually it shows a filter instantly responding to client in case of an error. In the latter case make sure the response is finished.

Models

Models are modules or classes describing data to be managed by your application. They are meant to be an interface for accessing data in an attached data storage like a database.

Hitchy doesn't know much about handling data itself. However, there is a pretty powerful plugin you definitely want to check out.

They are discovered in folder api/models of your application as well as any installed plugin.

Services

Services are modules or classes as well. They are meant to implement and provide features that are commonly required in controllers, policies and models. Whenever there is code to be used redundantly you should put it in a service.

Service components are discovered in folder api/services of your application as well as any installed plugin.

Exposure At Runtime

All components are exposed at runtime in section runtime of Hitchy's API. There are separate groups for every kind of component:

  • api.runtime.controllers
  • api.runtime.policies
  • api.runtime.models
  • api.runtime.services

In addition, in context of a controller's or a policy's request handler either collection of components is available via some convenient alias:

  • this.controllers
  • this.policies
  • this.models
  • this.services

Singular Collection Names

For the sake of convenience and integrity either collection can be addressed using singular names as well.

Derivation of Component Names

When exposing components Hitchy is deriving either component's resulting name from path name of Javascript file providing its implementation relative to the type of component's folder following this process:

  1. The file's extension .js is dropped.

  2. Leading digits optionally separated by a dash or underscore are stripped off.

    Leading digits can be used to optionally control the order of loading available modules during bootstrap e.g. to assure that some component is capable of accessing its previously loaded parent class.

  3. The segments of path name

  4. The resulting name

    • is converted to all lowercase letters and eventually
    • converted from kebab-case naming style into PascalCase naming style.

Example

Consider having a file api/services/01-converter-tool/archive/1_ZIP.js. This would result in the following value per step given before:

  1. 01-converter-tool/archive/1_ZIP
  2. converter-tool/archive/ZIP
    • ZIP/archive/converter-tool
    • ZIP-archive-converter-tool
    • zip-archive-converter-tool
    • ZipArchiveConverterTool

Thus the component implemented in assumed file would be exposed as api.runtime.services.ZipArchiveConverterTool.

Customizing Exposure

Starting with version 0.3.3 every plugin as well as the application may customize the way Hitchy is discovering and exposing their individual components.

Starting with version 0.4.0 this support for customizing exposure has been moved from configuration to meta information.

Accessing Components

For example, the following request handler is accessing a service component named FileZipper which is discovered in file api/service/file-zipper.js:

function someRequestHandler( req, res ) {
    res
        .status( 200 )
        .json( this.api.runtime.services.FileZipper.listFromArchive( "some/archive" ) );
}

Optionally, Hitchy's API is available via property hitchy of provided request descriptor:

function someRequestHandler( req, res ) {
    res
        .status( 200 )
        .json( req.hitchy.runtime.services.FileZipper.listFromArchive( "some/archive" ) );
}

Basically, req.hitchy and this.api are both referring to the same API instance. Using req.hitchy is beneficial when using arrow functions as well as on passing request descriptor req into sub-functions.

Last but not least aliases are provided in context of request handlers for accessing either type of component more conveniently:

function someRequestHandler( req, res ) {
    res
        .status( 200 )
        .json( this.services.FileZipper.listFromArchive( "some/archive" ) );
}