@northscaler/message-support
v0.2.0
Published
Messaging primitives for Node.js
Downloads
4
Readme
@northscaler/message-support
Message factories to use in your CQRS or asynchronous, message-based architectures.
Types of messages
This library supports three types of messages:
- request,
- response, and
- event
messages.
Key message features
There are three top-level sections to each message:
data
: the payload of the message, which is usually an object,meta
: data about the message, includingid
: the unique message id,origin
: the location (component & hostname) where the message was created,instant
: the instant in time the message was created,traceId
: a trace id that can be used for distributed tracing which can be given or is generated, andcorrelationId
: an id that, if present, indicates that something is awaiting a response,
error
: present if there is an error processing a message, includingname
: theError.name
value,message
: theError.message
value,stack
: if so configured (false by default), theError.stack
value,code
: theError.code
value, if present,info
: theError.info
value, containing contextual information provided with theError
, if present,cause
: if so configured (false by default), theError.cause
value, holding the cause of theError
, if present.
In addition, all message types have an action
in a type-dependent location.
- For a request message, it's in the
meta.request
section. - For an event message, it's the
meta.event
section. - For a response message, it's in the
meta.response
section.
Each message type may include other, type-specific fields as documented.
Custom data
The data
section is never inspected, as it is always application-specific.
Sometimes, applications need more than just an action
property in the message.
You are free to add properties anywhere you like after the factory has created the message.
Common properties like this include
scope
,domain
orcontext
, indicating some conceptual boundary that only applies to certain message processors and usually placed next toaction
,type
, indicating the type of the entity being manipulated for CRUD operations and is also usually placed next toaction
, andauth
,security
,securityContext
,user
oruserContext
, holding information about the authenticated user (like user id, roles played, scopes granted, etc) causing the activity and is usually placed directly in themeta
section.
Request messages
Use the RequestMessageFactory
to create a request message.
const { RequestMessageFactory } = require('@northscaler/message-support').factories
const pkg = require('.../package.json') // or whatever
const factory = new RequestMessageFactory({
componentName: pkg.name,
componentVersion: pkg.version
})
const message = factory.create({
data: { username: 'matthew', email: '[email protected]' },
action: 'CREATE_USER',
traceId: '456',
correlationId: '789'
})
console.log(JSON.stringify(message, null, 2))
/* logs something like:
{
"data": {
"username": "matthew",
"email": "[email protected]"
},
"meta": {
"id": "c6cac4e6-47e4-4e6c-b0eb-95984c04b9f3",
"instant": "2020-09-18T18:40:52.069Z",
"traceId": "456",
"origin": {
"component": "@northscaler/message-support",
"version": "0.1.0-pre.0",
"hostname": "rocky.local"
},
"request": {
"action": "CREATE_USER"
},
"correlationId": "789"
}
}
*/
Event messages
Use the EventMessageFactory
to create event messages.
There are two temporal kinds of event messages, past & future, and two kinds of past events, successful & unsuccessful.
The most common is a success event:
const { CodedError } = require('@northscaler/error-support')
const { EventMessageFactory } = require('../main/factories') // require('@northscaler/message-support').factories
const pkg = require('../../package.json') // or whatever
const TestError = CodedError({ name: 'TestError' })
const NestedTestError = CodedError({ name: 'NestedTestError' })
const factory = new EventMessageFactory({
componentName: pkg.name,
componentVersion: pkg.version,
includeErrorStacks: true,
includeErrorCauses: true
})
let message = factory.createSuccess({
data: { username: 'matthew', email: '[email protected]' },
action: 'CREATE_USER',
traceId: '456',
correlationId: '789'
})
/* logs something like:
{
"data": {
"username": "matthew",
"email": "[email protected]"
},
"meta": {
"id": "5cfc0636-c1a8-4878-b2fd-d954db7157ea",
"instant": "2020-09-18T19:10:56.072Z",
"traceId": "456",
"origin": {
"component": "@northscaler/message-support",
"version": "0.1.0-pre.0",
"hostname": "rocky.local"
},
"event": {
"did": "CREATE_USER",
"status": "SUCCESS"
}
}
}
*/
Another common one is a failure event:
message = factory.createFailure({
data: { username: 'matthew', email: '[email protected]' },
action: 'CREATE_USER',
error: new TestError({ message: 'boom', cause: new NestedTestError({ message: 'bang' }) }),
traceId: '456',
correlationId: '789'
})
console.log(JSON.stringify(message, null, 2))
/* logs something like:
{
"data": {
"username": "matthew",
"email": "[email protected]"
},
"error": {
"name": "TestError",
"code": "E_TEST",
"cause": {
"name": "NestedTestError",
"code": "E_NESTED_TEST",
"message": "E_NESTED_TEST: bang",
"stack": null
},
"message": "E_TEST: boom: E_NESTED_TEST: bang",
"stack": null
},
"meta": {
"id": "e6232595-35cd-461f-9488-a7b69efd31b1",
"instant": "2020-09-18T19:07:52.359Z",
"traceId": "456",
"origin": {
"component": "@northscaler/message-support",
"version": "0.1.0-pre.0",
"hostname": "rocky.local"
},
"event": {
"did": "CREATE_USER",
"status": "FAILURE"
}
}
}
*/
You can also send events indicating that something is about to happen:
message = factory.createFuture({
data: { username: 'matthew', email: '[email protected]' },
action: 'CREATE_USER',
traceId: '456',
correlationId: '789'
})
console.log(JSON.stringify(message, null, 2))
/* logs something like:
{
"data": {
"username": "matthew",
"email": "[email protected]"
},
"meta": {
"id": "d5f024da-2ad9-43a2-aa9c-7d992088db96",
"instant": "2020-09-18T19:14:46.625Z",
"traceId": "456",
"origin": {
"component": "@northscaler/message-support",
"version": "0.1.0-pre.0",
"hostname": "rocky.local"
},
"event": {
"will": "CREATE_USER"
}
}
}
*/
Response messages
Use the ResponseMessageFactory
to create response messages for request messages that include a correlationId
, indicating that something is expecting a response after the request is handled.
There are two kinds of response messages, successful & unsuccessful.
The most common is a successful response.
This example uses the convenience method createSuccessFromRequest
.
const { CodedError } = require('@northscaler/error-support')
const { ResponseMessageFactory, RequestMessageFactory } = require('../main/factories') // require('@northscaler/message-support').factories
const pkg = require('../../package.json') // or whatever
const TestError = CodedError({ name: 'TestError' })
const NestedTestError = CodedError({ name: 'NestedTestError' })
const requestFactory = new RequestMessageFactory({
componentName: pkg.name,
componentVersion: pkg.version,
includeErrorStacks: false,
includeErrorCauses: true
})
const responseFactory = new ResponseMessageFactory({
componentName: pkg.name,
componentVersion: pkg.version,
includeErrorStacks: false,
includeErrorCauses: true
})
// this is the request to which we're going to send a response message
const request = requestFactory.create({
data: { username: 'matthew', email: '[email protected]' },
action: 'CREATE_USER',
traceId: '456',
correlationId: '789'
})
// say the service we delegated to assigned id '246' to the user, so add that to the data
let message = responseFactory.createSuccessFromRequest({ request, data: { ...request.data, id: '246' } })
console.log(JSON.stringify(message, null, 2))
/* logs something like:
{
"data": {
"username": "matthew",
"email": "[email protected]",
"id": "246"
},
"meta": {
"id": "cdd0617b-d6f8-46be-ae84-700b86d7ac30",
"instant": "2020-09-18T19:25:33.464Z",
"traceId": "456",
"origin": {
"component": "@northscaler/message-support",
"version": "0.1.0-pre.0",
"hostname": "rocky.local"
},
"response": {
"status": "SUCCESS",
"elapsedMillis": 134
},
"correlationId": "789"
}
}
*/
Here is a failure response.
This example uses the convenience method createFailureFromRequest
.
message = responseFactory.createFailureFromRequest({
request,
data: request.data,
error: new TestError({ message: 'boom', cause: new NestedTestError({ message: 'bang' }) })
})
console.log(JSON.stringify(message, null, 2))
/* logs something like:
{
"data": {
"username": "matthew",
"email": "[email protected]"
},
"error": {
"name": "TestError",
"code": "E_TEST",
"cause": {
"name": "NestedTestError",
"code": "E_NESTED_TEST",
"message": "E_NESTED_TEST: bang",
"stack": null
},
"message": "E_TEST: boom: E_NESTED_TEST: bang",
"stack": null
},
"meta": {
"id": "840f5486-fe35-4661-9435-796aa0ce3483",
"instant": "2020-09-18T19:35:36.181Z",
"traceId": "456",
"origin": {
"component": "@northscaler/message-support",
"version": "0.1.0-pre.0",
"hostname": "rocky.local"
},
"response": {
"status": "FAILURE",
"elapsedMillis": 4
},
"correlationId": "789"
}
}
*/
There is also a more general method called create
to which these convenience methods delegate.