Programmatic API
The @platformatic/runtime package can be used to start, control, and inspect a Platformatic application from Node.js code, without going through the CLI. This is useful for tests, custom tooling, and embedding Platformatic in another application.
The API works with all Platformatic application types — service, db, gateway, composer, and runtime itself. Configurations that are not already a runtime configuration are automatically wrapped in one.
Getting started
import { create } from '@platformatic/runtime'
const app = await create('path/to/platformatic.runtime.json')
const entrypointUrl = await app.start()
const res = await fetch(entrypointUrl)
console.log(await res.json())
await app.close()
create() returns a Runtime instance. The instance exposes lifecycle, introspection, HTTP-injection, and per-application control methods documented below.
Top-level functions
create(configOrRoot, sourceOrConfig?, context?)
Builds a Runtime from a configuration file path or an in-memory configuration object. The returned runtime is not started — call start() (or pass context.start = true) to bring applications up.
When the configuration's $schema resolves to a non-runtime module (e.g. @platformatic/service, @platformatic/db, @platformatic/gateway), it is automatically wrapped in a runtime configuration so the same API works for any application type.
import { create } from '@platformatic/runtime'
const app = await create({
$schema: 'https://schemas.platformatic.dev/@platformatic/runtime/3.54.0.json',
entrypoint: 'main',
applications: [{ id: 'main', path: './main' }]
})
await app.start()
Equivalent $schema values that create() accepts and wraps transparently:
https://schemas.platformatic.dev/@platformatic/runtime/3.54.0.json
https://schemas.platformatic.dev/@platformatic/service/3.54.0.json
https://schemas.platformatic.dev/@platformatic/db/3.54.0.json
https://schemas.platformatic.dev/@platformatic/gateway/3.54.0.json
https://schemas.platformatic.dev/@platformatic/composer/3.54.0.json
By default create() installs signal handlers (SIGTERM/SIGINT via close-with-grace, and SIGUSR2 to trigger runtime.restart()). Pass context: { setupSignals: false } to opt out — recommended when embedding the runtime in tests or another process that owns its own signal handling.
loadConfiguration(configOrRoot, sourceOrConfig?, context?)
Reads a configuration file (or accepts an in-memory object), validates it against the runtime schema, applies the upgrade pipeline for older schemas, and resolves environment variables. The application type is auto-detected from the $schema; non-runtime configurations are wrapped in a runtime configuration via wrapInRuntimeConfig().
import { loadConfiguration } from '@platformatic/runtime'
const config = await loadConfiguration('/path/to/platformatic.config.json')
Use this when you need to inspect or mutate the resolved configuration before passing it to create().
prepareApplication(runtimeConfig, application)
Normalizes an application descriptor (resolving paths, detecting the capability type, applying defaults for watch, management, workers, localUrl, etc.) so it is ready to be passed to runtime.addApplications().
You must call prepareApplication() before adding an application at runtime — see Adding and removing applications at runtime.
wrapInRuntimeConfig(config, context?)
Wraps a single-application configuration (service, db, gateway, composer) into a synthetic one-application runtime configuration. Called automatically by create() and loadConfiguration(); exported for advanced use cases.
loadApplicationsCommands(executableName?)
Walks the applications declared in the nearest runtime configuration and aggregates any custom CLI commands they expose (via each capability's createCommands hook). Returns { applications, commands, help }. Used by the CLI to surface application-specific subcommands.
The Runtime instance
create() resolves to a Runtime instance. Its methods are listed below.
Lifecycle
runtime.start(silent = false): Promise<string | undefined>— Starts all applications. Returns the entrypoint's external URL (orundefinedif no entrypoint binds an external port). Ifinit()hasn't been called yet,start()calls it.runtime.stop(silent = false): Promise<void>— Stops all applications. The entrypoint is stopped first so it stops accepting new requests immediately.runtime.close(silent = false): Promise<void>— Stops applications and tears the runtime down completely (closes the management API, broadcast channels, dispatcher, etc.). Afterclose()the runtime cannot be restarted; create a new instance.runtime.restart(applications?: string[]): Promise<string | undefined>— Restarts every application (or only the IDs inapplications). Returns the entrypoint URL once the restart completes.runtime.init(): Promise<void>— Performs one-time setup (loads capabilities, prepares workers). Usually called transitively bystart(); call it explicitly only if you need the runtime ininit'ed state without starting applications.
HTTP injection
runtime.inject(id, injectParams): Promise<InjectResponse>
Dispatches an HTTP request straight into an application by its id, without going through the network. Behaves like Fastify's inject and is the recommended way to write integration tests against a runtime.
import { create } from '@platformatic/runtime'
const app = await create('path/to/watt.json', { setupSignals: false })
await app.start()
const res = await app.inject('main', {
method: 'POST',
url: '/items',
headers: { 'content-type': 'application/json' },
body: { name: 'widget' }
})
console.log(res.statusCode, JSON.parse(res.body))
await app.close()
injectParams accepts a plain URL string as shorthand, or an object with method, url, headers, query, and body. When content-type: application/json is set, an object body is automatically JSON.stringify'd.
The response object exposes statusCode, statusMessage, headers, body (string), payload (alias of body), and rawPayload (ArrayBuffer).
Introspection
runtime.getUrl(): string | undefined— The entrypoint's external URL once started.runtime.getRuntimeStatus(): string— One ofstarting,started,stopping,stopped,closed.runtime.getRuntimeMetadata(): Promise<RuntimeMetadata>—pid,cwd,argv,uptimeSeconds,execPath,nodeVersion,projectDir,packageName,packageVersion,url,platformaticVersion.runtime.getRuntimeConfig(includeMeta = false): object— The resolved configuration. WhenincludeMetaistruethe[kMetadata]symbol is preserved (needed byprepareApplication()).runtime.getRuntimeEnv(): Record<string, string>— Environment variables visible to the runtime process.runtime.getApplicationsIds(): string[]— IDs of all configured applications.runtime.getApplicationDetails(id, allowUnloaded = false): Promise<ApplicationDetails>— Per-application info:type,status,dependencies,version,localUrl,entrypoint,workers,url.
Per-application control
runtime.startApplication(id, silent = false): Promise<void>runtime.stopApplication(id, silent = false): Promise<void>runtime.restartApplication(id): Promise<void>
These act on a single application by id. The entrypoint can be restarted but cannot be removed (see below).
Adding and removing applications at runtime
The runtime supports adding and removing applications after start() has been called.
runtime.addApplications(applications, start = false)
Registers new applications on a running runtime. If start is true, the new applications are started in parallel; otherwise they remain stopped until startApplication() is called.
Each entry in applications must be processed through prepareApplication() first — it normalizes paths, detects the capability type, and applies defaults the runtime expects.
import { create, prepareApplication } from '@platformatic/runtime'
const app = await create('path/to/watt.json')
await app.start()
const newApplications = [
await prepareApplication(app.getRuntimeConfig(true), {
id: 'analytics-service',
path: './analytics',
workers: 2
})
]
await app.addApplications(newApplications, true)
Pass app.getRuntimeConfig(true) (with includeMeta: true) so prepareApplication() can resolve relative paths against the runtime's root.
runtime.removeApplications(applications, silent = false)
Stops the listed applications and removes them from the runtime. applications is an array of application IDs. Set silent to true to suppress logging.
await app.removeApplications(['analytics-service'])
The entrypoint cannot be removed; attempting to do so throws a CannotRemoveEntrypointError.
Example: dynamic application management
import { create, prepareApplication } from '@platformatic/runtime'
const app = await create('path/to/watt.json')
await app.start()
const newService = await prepareApplication(app.getRuntimeConfig(true), {
id: 'analytics-service',
path: './analytics',
workers: 2
})
await app.addApplications([newService], true)
// Later, when no longer needed
await app.removeApplications(['analytics-service'])
Other exports
The package also exports:
Runtime— the class itself, forinstanceofchecks and advanced subclassing scenarios.Generator,WrappedGenerator— generators used bycreate-platformaticto scaffold new runtimes.schema— the JSON Schema for runtime configuration.transform— the configuration transform pipeline used internally.errors— a namespace of@fastify/errorconstructors (ApplicationNotFoundError,MissingEntrypointError,CannotRemoveEntrypointError, etc.).symbols— internal symbols (kConfig,kId,kITC, ...) used to attach metadata to configuration and worker objects.version— the package version string.
Issues
If you run into a bug or have a suggestion for improvement, please raise an issue on GitHub or join our Discord feedback channel.