@kultify/kmicro-js
v0.2.1
Published
Simple microservice framework based on [NATS micro](https://github.com/nats-io/nats.js/tree/main/services).
Readme
kmicro-js
Simple microservice framework based on NATS micro.
Features:
- Request/response RPC over NATS with binary payloads
- OpenTelemetry distributed tracing (context propagation across service calls)
- Automatic header propagation through call chains
- Call depth limiting (max 20) to prevent infinite loops
- Pino JSON logging with trace/span correlation
Install
npm install @kultify/kmicro-jsOpenTelemetry Setup
kmicro-js uses the OpenTelemetry API to create spans and propagate trace context. It does not initialize the OTel SDK — your application is responsible for that.
If no SDK is registered, tracing becomes a no-op.
Create an instrumentation.ts file and load it before any other imports:
// instrumentation.ts
import { NodeSDK } from "@opentelemetry/sdk-node";
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-proto";
import { resourceFromAttributes } from "@opentelemetry/resources";
import { PinoInstrumentation } from "@opentelemetry/instrumentation-pino";
import { ATTR_SERVICE_NAME } from "@opentelemetry/semantic-conventions";
import { ATTR_MESSAGING_SYSTEM } from "@opentelemetry/semantic-conventions/incubating";
const sdk = new NodeSDK({
resource: resourceFromAttributes({
[ATTR_SERVICE_NAME]: "my-service",
[ATTR_MESSAGING_SYSTEM]: "nats",
}),
traceExporter: new OTLPTraceExporter(),
instrumentations: [
new PinoInstrumentation({ disableLogSending: true }),
],
});
sdk.start();Then load it first via Node's --import flag:
node --import ./instrumentation.js ./main.jsRequired OTel packages for the application:
npm install @opentelemetry/sdk-node @opentelemetry/exporter-trace-otlp-proto \
@opentelemetry/resources @opentelemetry/semantic-conventions \
@opentelemetry/instrumentation-pinoUsage
import { init } from "@kultify/kmicro-js";
const service = await init("nats://localhost:4222", "my-service", "0.0.1");
// Register an endpoint
service.addEndpoint("greet", async (context, data) => {
const request = JSON.parse(Buffer.from(data).toString());
const logger = context.getLogger("greet");
logger.info({ request }, "received greeting request");
return Buffer.from(JSON.stringify({ hello: request.name }));
});
// Call another service
const result = await service.call(
"other-service.action",
Buffer.from(JSON.stringify({ key: "value" })),
);
// Call with custom headers and timeout
const result2 = await service.call(
"other-service.action",
Buffer.from(JSON.stringify({ key: "value" })),
{
timeout: 10000,
header: { "X-Auth": "token" },
},
);
// Downstream calls from within a handler preserve trace context and headers
service.addEndpoint("orchestrate", async (context, data) => {
const downstream = await context.call(
"other-service.action",
Buffer.from(JSON.stringify({ foo: "bar" })),
);
return downstream;
});
// Graceful shutdown
await service.stop();License
MIT
