Skip to main content

Lifecycle

Lifecycle

Application lifecycle managent is the main feature of Basica. The following flowchart represents how it works.

For a service or entrypoint to be considered part of the lifecycle, it has to be registered in configureLifecycle().

Startup

A startup item is a service that performs a one off operation, such as running database migrations or loading a ML model into memory.

service.ts
import { IStartup } from "@basica/core";

export class MyService implements IStartup {

async start(signal: AbortSignal) {
// run migrations
}
}
index.ts
// ...
const app = AppBuilder.registerDependencies()
.configureLifecycle((b, c) =>
b.addStartup("my-service", () => c.myService)
)
.build();
// ...

Shutdown

A shutdown item is a service that performs a cleanup of resources before shutdown, such as closing a database connection or stopping a server from accepting requests.

service.ts
import { IShutdown } from "@basica/core";

export class MyService implements IShutdown {

async shutdown(signal: AbortSignal) {
// close db connection
}
}
index.ts
// ...
const app = AppBuilder.registerDependencies()
.configureLifecycle((b, c) =>
b.addGracefulShutdown("my-service", () => c.myService)
)
.build();
// ...

Entrypoint

An entrypoint is a service that performs both startup and shutdown operations, from which external external events interact with your application. It should be used as the starting point for your business logic, such as a message broker subscriber or an api server.

service.ts
import { IEntrypoint } from "@basica/core";

export class MyService implements IEntrypoint {

async shutdown(signal: AbortSignal) {
// listen for events from the msg broker
}

async shutdown(signal: AbortSignal) {
// stop listening
}
}
index.ts
// ...
const app = AppBuilder.registerDependencies()
.configureLifecycle((b, c) =>
b.addEntrypoint("my-service", () => c.myService)
)
.build();
// ...

Running the app

Once the lifecycle is configured, a platform runner hands the process over to Basica. For a long-running Node process that's run from @basica/platform-node:

  • it starts every registered service and entrypoint; if startup fails it logs the error and exits with code 1;
  • it installs graceful-shutdown handlers, so a control signal (SIGINT/SIGTERM) or an empty event loop stops the lifecycle in reverse order (as shown in the flowchart above), exiting 0 on a clean shutdown or 1 on failure or timeout.
index.ts
import { run } from "@basica/platform-node";

const app = AppBuilder.registerDependencies()
// ...
.build();

run(app);

Because the runner owns process.exit and the signal handlers, it's not meant for tests, or for embedding an app inside a larger process. For those, drive the lifecycle yourself:

// `app` is your built AppBuilder result
const started = await app.lifecycle.start();
// interact with app.services / app.entrypoints ...
const stopped = await app.lifecycle.stop();

See Testing for the full pattern.