Middlewares

Learn how you can extend Jovo by hooking into different types of middlewares.

Introduction

Jovo offers a middleware architecture that lets you hook into certain steps in the lifecycle. This allows Jovo developers to extend the framework without having to make any adjustments to its core code.

Middlewares can be listened to in two different ways:

  • Hooks: Lightweight extensions for logging and simple data manipulation
  • Plugins: Powerful extensions that can hook into multiple middlewares

For example, a hook that does an API call for each new session might look like this:

app.hook('before.dialogue.start', async (jovo: Jovo): Promise<void> => {
  if (jovo.$session.isNew) {
    const response = await someApiCall();
    // ...
  }
});

The most common middlewares to be used are RIDR middlewares, but there are also other types of middlewares like event middlewares.

Types of Middlewares

There are two types of middlewares:

RIDR Middlewares

The RIDR lifecycle (Request - Interpretation - Dialogue & Logic - Response) is the main process that determines when each part of the Jovo app is executed.

When extending Jovo, you usually hook into one of the RIDR middlewares that are detailed in the below table:

MiddlewareDescription
request.startEnters the request middleware group
requestTurns the raw JSON request into a $request object
request.endLeaves the request middleware group with propagated $request object
interpretation.startEnters the interpretation middleware group
interpretation.asrASR/SLU integrations turn speech audio into raw text
interpretation.nluNLU integrations turn raw text into structured input
interpretation.endLeaves the interpretation middleware group with propagated $nlu object
dialogue.startEnters the dialogue middleware group
dialogue.routerUses information from the interpretation steps to find the right component and handler
dialogue.logicExecutes the component and handler logic
dialogue.endLeaves the dialogue middleware group with propagated $output array
response.startEnters the response middleware group
response.outputTurns $output into a raw JSON response
response.ttsTTS integrations turn text into speech output
response.endLeaves the response middleware group with propagated $response object

You can also learn more about the middleware code here: Middleware, MiddlewareCollection.

Event Middlewares

Event middlewares don't follow a linear process like the RIDR middlewares: They get executed whenever a specific method gets called, so this can happen multiple times during one RIDR lifecycle.

There are two types of event middlewares:

  • Public methods like $resolve can be accessed using event.$resolve
  • Some "under the hood" methods can be accessed using the class name and the method name, for example event.ComponentTreeNode.executeHandler

These middlewares can also come with a payload that you can access in your hook or plugin as second parameter, for example:

app.hook('event.$resolve', async (jovo: Jovo, payload): Promise<void> => {
  const resolvedHandler = payload.resolvedHandler;
  // ...
});

Find all current event middlewares in the table below:

MiddlewareDescriptionPayload
event.$resolve$resolve is called in a handlerresolvedHandler: string, eventName: string, eventArgs: ARGS extends unknown[]
event.$redirect$redirect is called in a handlercomponentName: string, handler: string
event.$delegate$delegate is called in a handlercomponentName: string, options: DelegateOptions
event.$send$send is called in a handleroutputConstructorOrTemplateOrMessage, options
event.ComponentTreeNode.executeHandlerThis event is called whenever a new handler gets executed. Part of the ComponentTreeNode class. See the ComponentTree section for more information.componentName: string, handler: string

Middleware Features

Custom Middlewares

You can also use the $handleRequest object to run your own middlewares, for example:

await jovo.$handleRequest.middlewareCollection.run('<YOUR_MIDDLEWARE_NAME>', jovo, payload);

The payload is of the type AnyObject, so you can pass any object to the middleware, for example { name: 'SomeName' }.

Using a hook or a plugin, you can then hook into this middleware:

app.hook('<YOUR_MIDDLEWARE_NAME>', async (jovo: Jovo, payload): Promise<void> => {
  // ...
});

Skip Middlewares

You can skip RIDR middlewares for the current request lifecycle by using the skipMiddlewares() method:

someMethod(jovo: Jovo): void {
  // ...

  // Skip single middleware
  jovo.$handleRequest.skipMiddlewares('interpretation.nlu');

  // Skip multiple middlewares
  jovo.$handleRequest.skipMiddlewares('interpretation.nlu', 'after.interpretation.nlu'); // Either add multiple strings or one array of strings
}

Stop the Middleware Execution

Either a hook or a plugin can use stopMiddlewareExecution to remove all middlewares from the middleware collection of HandleRequest and its plugins. This way, all following middlewares won't be executed.

Here is an example how this could look like for a plugin method (that was registered with a middleware inside mount):

someMethod(jovo: Jovo): void {
  // ...
  jovo.$handleRequest.stopMiddlewareExecution();
}