Skip to content

Implementation

The implementation of a service primarily consists of defining the corresponding version and a (asynchronous) handler function that processes incoming messages and returns an appropriate result.

import { createService } from '@requence/service'
createService('1.0.0', async (context) => {
// process incomming message, return result
return "someResult"
})

The handler function takes a single argument: the service context. Through the context, you can access the incoming data (among other options).

import { createService } from '@requence/service'
createService('1.0.0', async (context) => {
const myInput = context.getData().myInput
return "someResult"
})

the service context also includes the service configuration. This is usefull for informations like api keys.

import { createService } from '@requence/service'
createService('1.0.0', async (context) => {
const { openAiApiKey } = context.getConfiguration()
return "someResult"
})

By default, the return value of the handler function is always sent to the standard output. If the service has been configured with multiple outputs, the result can also be sent to a different output.

import { createService } from '@requence/service'
createService('1.0.0', async (context) => {
// this will send the result to a named output...
context.toOutput("myOtherOutput", "someOtherResult")
// ... and this will additionally send the result to the default output
return "someResult"
})
import { createService } from '@requence/service'
createService('1.0.0', async (context) => {
const taskInput = context.getInput()
const taskMeta = context.getMeta()
return "someResult"
})

During the execution of a task, the service may be called multiple times. The service can store data on a state object, which remains valid for the duration of the task.

This is especially useful when there are multiple instances of the service and it cannot be guaranteed that the same instance will be called again on subsequent invocations.

import { createService } from '@requence/service'
createService('1.0.0', async (context) => {
const invocationCount = context.state.invocations ?? 0
context.state.invocations = invocationCount + 1
return "someResult"
})

The service has the ability to send debug information to the Requence backend. This information is visible in the task overview.

import { createService } from '@requence/service'
createService('1.0.0', async (context) => {
context.debug.info("this is just an info…")
context.debug.log({ logMessage: '… this will log an object…'})
context.debug.warn("… this is a warning…")
context.debug.error("… and finally this is an error")
return "someResult"
})

In some situations, a service may not be able to process a request (e.g., when a required resource like a database is unavailable). Instead of throwing an exception, there are special functions available to either abort the execution or retry it at a later time.

import { createService } from '@requence/service'
createService('1.0.0', async (context) => {
// This aborts the execution. If there is no “on fail” path defined in the corresponding task template, the task will fail.
context.abort("optional reason")
// Alternatively, you can retry the execution after an optional delay.
context.retry(3000) // ms
// the return value will never be reached in this case
return "someResult"
})

Although the handler function is asynchronous, there may be circumstances that significantly delay processing (e.g., waiting for user interaction). In such cases, processing can be deferred and resumed at a later time.

import { createService } from '@requence/service'
const userInteractions = new Map<string, string>()
// save a reference to the service
const service = createService('1.0.0', async (context) => {
const userInteractionKey = createUserInteraction(context.getData().question)
// generate a defer key for later use
const deferKey = context.defer("waiting for user interaction (optional reason)")
userInteractions.set(userInteractionKey, deferKey)
// no return necessary
})
// some time later
// retrieve persisted defer key
const persistedDeferKey = userInteractions.get(completeUserInteractionKey)
service.act(persistedDeferKey, api => {
api.send('this is a deferred result')
})