Registers a folder of handlers as HTTP POST routes on a server - handlers receive commands/events in CloudEvents format.
View on npm.
npm install --save register-server-handlers
On startup, call registerHandlers
import path from 'path'
import express from 'express'
import { registerHandlers } from 'register-server-handlers'
const server = express()
export const start = async (server) => {
// create post handlers for each file in folder src/handlers at `/<filename without extension>`
await registerHandlers({
server,
path: path.resolve(process.cwd(), 'src', 'handlers'),
handlerOptions: {
sync: true
}
})
// create post handlers for cloudevents with same handlers at `/cloudevents/<filename without extension>`
await registerHandlers({
server,
path: path.resolve(process.cwd(), 'src', 'handlers'),
cloudevents: true,
serverPath: '/cloudevent/'
})
try {
await server.listen(port)
} catch (err) {
server.log.error(err)
process.exit(1)
}
}
start(server)
This way you can write the handler logic once and handle it via direct HTTP Post or via a CloudEvent, such as when creating a Trigger
for a Knative Event, or from a Command API, such as Hasura Actions.
Each handler in the given directory must export a named function handle
, and optionally, a where
filter.
For example, src/handlers/example.initialize.js
:
// if message.data.id is not provided, a 202 response will be sent and the handler will not execute
// makes for easy, declarative validation and unit testing
// https://github.com/knative/specs/blob/main/specs/eventing/data-plane.md#event-acknowledgement-and-delivery-retry
export const where = (message) => !!(message.data && message.data.id)
// `message` is a CloudEvent
// `type` is the file name without the extension - `example.initialize`
export const handle = async (request, reply, message, handlerOptions) => {
const { data, type, source } = message
const { id } = message
const { sync } = handlerOptions
// do stuff
// then reply
reply.status(200).send()
}
This will register /example.initialize
as an HTTP Post endpoint.
If you expect to receive messages in the CloudEvents format, you can set cloudevents: true
.
await registerHandlers({
server,
path: path.resolve(process.cwd(), 'src', 'handlers'),
cloudevents: true
})
The default server path is /
, which the filename without the extension is appended to.
To customize this path, set serverPath
to a custom value.
For example, if you want to receive cloud events at /cloudevent/${eventname}
await registerHandlers({
server,
path: path.resolve(process.cwd(), 'src', 'handlers'),
cloudevents: true,
serverPath: '/cloudevent/'
})
The intended usage for this is to build CQRS/ES systems with KNative. If you're interested in doing something similar, this library works well with knativebus.
Configured correctly, sending cloudevents to the cloudevent handlers via knativebus can be accomplished with:
await bus.send('example.initialize', { id: 1, name: 'Example 1' })
This would Trigger
a handler on a service subscibed to the example-commands
broker (see knative docs for examples of creating brokers). The handler would receive a cloudevent with type example.initialize
, therefore handled by src/handlers/example.initialize.js
in the above configuration.
apiVersion: eventing.knative.dev/v1
kind: Trigger
metadata:
name: example.initialize
spec:
broker: example-commands
filter:
attributes:
type: example.initialize
subscriber:
ref:
{{- if .Values.knativeDeploy }}
apiVersion: serving.knative.dev/v1
kind: Service
name: example-model
{{- else }}
apiVersion: v1
kind: Service
name: example-model
{{- end }}
uri: /cloudevent/example.initialize