Skip to main content

Quick start

Create a passive module for a finite task, install it, and run the service:

import {
Microservice,
type MicroserviceContext,
MicroserviceModule,
ModuleKind,
port,
} from '@microde/microservice';

class GreetingModule extends MicroserviceModule {
readonly kind = ModuleKind.Passive;

constructor(context: MicroserviceContext) {
super(context);
}

async run(): Promise<void> {
console.log('Hello from Microde');
}

async stop(): Promise<void> {}
}

const service = new Microservice();

service.install((context) => new GreetingModule(context));

const result = await service.run();
process.exitCode = result.exitCode;

run() resolves only after the lifecycle has finished, including teardown, shutdown, and cleanup. Lifecycle failures are returned in the result rather than thrown from the returned promise:

const result = await service.run();

if (result.error !== undefined) {
console.error(result.error);
}

process.exitCode = result.exitCode;

An individual Microservice can run only once. Install every module before calling run().

Module constructors should accept MicroserviceContext, not the concrete Microservice class. The context exposes non-blocking requestStop() and panic() operations without coupling the module to lifecycle coordination APIs such as install(), run(), public stop(), or state.

Named dependencies

Install named instances and bind relationship slots explicitly before calling run():

const databasePort = port<Database>('database');
const database = service.install(
'database',
(context) => new DatabaseModule(context),
);
const orders = service.install(
'orders',
(context) => new OrdersModule(context, databasePort),
);
service.bind(orders, 'database', database);

Microde validates all bindings, rejects dependency cycles, and publishes provider resolutions atomically when run() begins. References may be cyclic, but they are not part of lifecycle ordering and cannot be read during setup.