Serviceberry
  • Guides
  • API Reference
  • Blog

›Guides

Guides

  • Getting Started
  • Handlers
  • Plugins
  • Serializers and Deserializers
  • Service Tree
  • Auto Responses
  • How It Works

API Reference

  • Serviceberry
  • Trunk
  • Branch
  • Leaf
  • Request
  • Response
  • HttpError

Handlers

Understanding handlers is central to creating a service using Serviceberry. All things that interact with service requests do so through a common handler API. This means your endpoints, plugins, error coping, serializers and deserializers are all created in the same way. This makes creating services easier and it makes creating plugins easier.

A handler can be a function, or an object, or a promise that resolves to a handler function or object. Handler objects can be any object with a use method that is a handler function.

Handler Functions

Handler functions all have the same arguments signature (request, response) and ability to control requests. Request and response are wrapper objects for the http.IncomingRequest and http.ServiceResponse arguments passed to the underlining http.Server request event listener.

  • request object
  • response object

Handlers are added to the trunk, branches, and leaves of your service using methods use, cope and on. When Serviceberry routes each request through your service, it builds a queue of handlers. Each handler in the queue is then called one after the other. Each given control of the request and response until control is yielded to the next handler. Handlers yield control through request and response methods, by the value they return, or by throwing an error.

Three Possible Outcomes

Each handler has exactly one opportunity to control the request and response. Once action has been taken there are three possible outcomes.

  1. Proceed with the request (typical for a plugin, serializer, or deserializer).
  2. Complete the request and send a response to the client (typical for an endpoint).
  3. Fallback to the nearest error coping handler.

Proceeding (1) or falling back (3) could implicitly result in sending a response when there are no more handlers in the queue

Controlling the Outcome

Which outcome occurs depends primarily on the value returned from a handler. A handler can control the request and response by the following actions.

  • Return nothing and call request.proceed([result]).
  • Return the result.
  • Return a promise for the result (any thennable should work).

Calling request.proceed([result]), returning anything else other than an error, and returning a promise that resolves to the result are all functionally equivalent. They all cause the request to proceed. The next handler in the queue will be called and control will be given to it. If the queue has no more handlers, the request will end and response.send([options]) is call implicitly.

  • Return nothing and call response.send([options]).

Returning nothing and calling response.send([options]) will complete the request (as far as the handler queue is concerned) and begin the process of serializing the writing the response to the client.

  • Return nothing and call request.fail(error[, status[, headers]]). Throws the resulting error halting execution of the handler.
  • Return, resolve to, or reject with an error.
  • Throw an error.

Calling request.fail(error[, status[, headers]]), returning an error, throwing an error, or returning a promise that is rejected with an error or resolves to an error are all functionally equivalent. They all cause control to fallback to the nearest error coping handler. If no error coping handler is found, the request will be end and response.send([options]) is call implicitly sending the error to the client. Errors are available in error coping handlers at request.error.

The first of any of the actions above after a handler is called and the handler's control is yielded. When control is yielded, request.proceed([result]), request.fail(error[, status[, headers]]), and response.send([options]) methods given to the handler are all rendered useless.

request.proceed([result])), request.fail(error[, status[, headers]]), and response.send([options])) can all be called asynchronously as long as a handler returns nothing or a promise. Additionally none are required to be called with context so each can be safely passed directly as callbacks - such as .then(request.proceed, request.fail).

Handler Objects

Everywhere a handler is accepted as an argument, an object can be used. It can be any object that has a use method that is a handler function. When Serviceberry is passed a handler object it binds the object's use method to the object so that handlers can be stateful. This can be particularly useful for plugins.

Handler Promises

Everywhere a handler is accepted as a argument, a promise can be used. The promise must resolve to a handler function or a handler object. The service trunk will wait until all handlers are resolved before starting.

To learn about using and creating plugins, checkout the next guide.

← Getting StartedPlugins →
  • Handler Functions
    • Three Possible Outcomes
    • Controlling the Outcome
  • Handler Objects
  • Handler Promises
Guides
Getting StartedHandlersPluginsSerializers and DeserializersSerice TreeAuto ResponsesHow it Works
API Reference
ServiceberryTrunkBranchLeafReqeustResponseHttpError
More
BlogGitHubStarContributingLicense