AI Agent Guide¶
Purpose: generate a correct Node.js polyglot function from this page alone. Humans: the narrative versions live in Function Writing Patterns and the join chapters. Orchestration (flows, graphs) is authored on the engine — use the engine AI guides.
Pre-write checklist¶
- Route name: lowercase
[a-z0-9._-], at least one period. Example:order.enrich. - Never block the event loop: async APIs only (
fs/promises,fetch); no*Synccalls in handlers; no unbounded CPU loops. - Function is stateless. State belongs to the calling flow/graph.
- Orchestration-shaped logic (sequencing, retries, branching) → STOP; author an Event Script flow or MiniGraph graph on the engine instead.
The contract¶
import { AppException, PostOffice, annotateTrace, getLogger, getTrace,
platform, preload } from 'mercury-composable';
const log = getLogger('my-app');
preload('order.enrich', { instances: 10 }, // { isPrivate: true } -> in-app only
async (headers, body) => {
// 1. validate; intentional errors = throw new AppException(status, message)
if (typeof body !== 'object' || body === null || typeof body.id !== 'string') {
throw new AppException(400, "missing 'id'");
}
// 2. work (async I/O only)
// 3. optional telemetry
annotateTrace('source', 'node.js'); // rides back on the reply
// 4. return the reply body (or an EventEnvelope for status/header control)
return { id: body.id, enriched: true };
});
await platform.run();
Rules:
headers: Record<string, string>;body: unknown(any MsgPack value). Narrowbodybefore use (typeof,in); never assume its shape.- Return value = reply body (a promise is awaited). Return
EventEnvelopeonly when setting status/headers. throw new AppException(status, message)→ envelope status + message (portable error). Unexpected exception → 500 + message + stack. Never return HTTP-shaped objects like{ status: 400 }.getTrace()→{ traceId, tracePath, cid, annotations }orundefined.- Reserved inbound header
my_correlation_id= the caller's business correlation id (read-only). Never send headers namedmy_*orx-event-api.
Composition (calling other functions)¶
// local (same app; reaches isPrivate routes):
const reply = await new PostOffice().request('other.route', { ... }, { timeoutMs: 5000 });
// drop-n-forget: po.send(...) -> 202 ack envelope
// remote peer or engine:
const po = new PostOffice('http://host:8085/api/event');
- Always check
reply.getStatus(); errors are envelopes, not exceptions. - Local calls reach
isPrivateroutes; the wire cannot (403). - For tests, pass a fresh registry:
new PostOffice(undefined, {}, registry).
Run + configure¶
node dist/src/cli.js app.mjs # config: resources/application.yml
node dist/src/cli.js app.mjs -Dkey=value # runtime override (engine syntax)
Well-known keys (full table: Configuration Reference):
application.name, rest.server.port (default 8085), log.format
(text|json|compact), log.level, info.app.version, info.app.description,
show.env.variables, show.application.properties,
mandatory.health.dependencies, optional.health.dependencies.
Health check function (engine interface contract)¶
preload('my.health', { instances: 5, isPrivate: true }, async (headers, _body) => {
if (headers.type === 'info') {
return { service: 'my.dependency', href: 'http://backend' };
}
return 'my.dependency is running fine'; // non-200 reply marks it DOWN
});
List the route in mandatory.health.dependencies (or optional.…).
HTTP surface (served by the host, no code needed)¶
POST /api/event (envelope wire) · GET / /info /info/routes /env /health
/livenessprobe. Shapes: HTTP Surface Reference.
Engine-side wiring (for completeness; authored on the engine)¶
# application.properties: yaml.event.over.http=classpath:/event-over-http.yaml
event.http:
- route: 'order.enrich'
target: 'http://node-host:8087/api/event'
Flow task process: 'order.enrich' or graph node
{"skill": "graph.task", "task": "order.enrich", ...} (engines ≥ v4.11.11 for
graph.task). Details: Join an Event Script Flow ·
Join a Knowledge Graph.
DO / DON'T¶
| DO | DON'T |
|---|---|
async APIs (fs/promises, fetch) |
*Sync calls or CPU loops on the loop |
throw new AppException(...) |
return { status: 400, ... } objects |
| keep functions stateless | cache business state in module globals |
| compose one or two leaf helpers | re-implement flows/retries in Node.js |
| let deadlines fail fast (408 envelope) | swallow timeouts and hoard work |