@setu-ts/microservice-starter

Opinionated plugin composition for building microservices with Setu-TS.

Extends the REST starter bundle with additional capabilities essential for distributed systems: messaging, queue processing, resilience patterns, and telemetry. Ideal for service-oriented architectures requiring async communication, circuit breakers, and observability.

Installation

npm install @setu-ts/microservice-starter
# Deno: deno add npm:@setu-ts/microservice-starter

Or via npm/yarn/pnpm:

npm install @setu-ts/microservice-starter

Usage

The starter exports createMicroserviceApp — a fully wired application combining REST plugins plus microservice-specific features:

import { createMicroserviceApp } from '@setu-ts/microservice-starter';

const app = createMicroserviceApp();

app.router.get('/hello', (ctx) => ctx.response.text('Hello world'));

await app.start({ port: 3000 });

With Options

Customize all plugin configurations through the optional options parameter:

import { createMicroserviceApp } from '@setu-ts/microservice-starter';
import type { MicroserviceStarterOptions } from '@setu-ts/microservice-starter';

const options: MicroserviceStarterOptions = {
  // REST base plugins (see rest-starter)
  config: {/* ... */},
  logger: {/* ... */},

  // Microservice additions
  messaging: {/* messaging plugin options */},
  queue: {/* queue plugin options */},
  resilience: {/* resilience plugin options */},
  telemetry: {/* telemetry plugin options */},
};

const app = createMicroserviceApp(options);

Advanced Plugin Composition

Use buildMicroservicePlugins together with createApplication from the kernel to construct a custom plugin array with full control over ordering and configuration:

import { buildMicroservicePlugins } from '@setu-ts/microservice-starter';
import { createApplication } from '@setu-ts/kernel';

const app = createApplication({
  plugins: buildMicroservicePlugins({
    messaging: {}, // provide options object; omit to use default
    queue: {/* custom config */},
    // telemetry omitted to exclude (no-op default)
  }),
});

Options

MicroserviceStarterOptions extends RestStarterOptions, so every REST option above applies here unchanged. Four fields are added, one per plugin this tier registers on top:

OptionTypeOmitted →
messagingMessagingPluginOptionsin-memory broker default
queueQueuePluginOptionsmemory adapter default
resilienceResiliencePluginOptionsplugin defaults
telemetryTelemetryPluginOptionsno-op — no exporter configured

Unlike the REST tier’s gated arms, these four plugins are always registered; the options only configure them. That is what makes createMicroserviceApp() with no arguments a working service rather than an inert one.

Included Plugins

CategoryPluginDescription
REST BaseRuntimePluginCore runtime integration
ConfigPluginConfiguration management
LoggerPluginStructured logging
ValidationPluginRequest validation
HttpSecurityPluginSecurity headers
HealthPluginHealth check endpoints
MetricsPluginMetrics collection
OpenApiPluginOpenAPI documentation
DecoratorPluginDecorator-based routing
DatabasePluginOptional database access
AuthPluginOptional authentication
MicroserviceMessagingPluginAsync message bus support
QueuePluginBackground job queueing
ResiliencePluginCircuit breaker & retries
TelemetryPluginTracing & observability

OpenApiPlugin self-hosts Swagger UI by default, so install swagger-ui-dist (^5) in the consuming application, or pass openapi: { swagger: { assets: 'cdn' } }. Without either, startup fails with OptionalDependencyUnavailableError.

The inherited auth arm accepts any AuthPluginOptions with at least one passive strategy (jwt, apiKey, session, or strategies). When present, AuthPlugin installs authentication middleware globally at priority 300 unless auth.middleware disables or moves it.

All microservice plugins are enabled by default; individual plugins can be configured or omitted via options.

Workers Portability

This starter bundles MessagingPlugin and QueuePlugin, which require raw network sockets (TCP/UDP) and are therefore not compatible with Cloudflare Workers. This starter is for Node.js, Deno, or Bun runtimes only — matching the CLI’s refusal of --template microservice --runtime cloudflare-workers. The REST base plugins (Runtime, Config, Logger, etc.) are all edge-safe.

Multi-instance Restriction + Escape Hatch

The four multi-instance plugins (cache, database, queue, messaging) accept an options.name parameter that creates a derived capability token. The starter registers one instance per arm on the bare token (e.g., CAPABILITIES.CACHE, CAPABILITIES.MESSAGING). Setting name through a starter arm moves the plugin off the bare token, which will break any code that resolves the capability (including health checks and documentation examples).

The starter does not support setting name through its option arms. If you need a second instance (e.g., a session cache distinct from the default, or a separate queue for dead-letter processing), register it manually after the starter returns:

import { createMicroserviceApp } from '@setu-ts/microservice-starter';
import { CachePlugin } from '@setu-ts/cache-plugin';
import { QueuePlugin } from '@setu-ts/queue-plugin';

const app = createMicroserviceApp();
app.register(CachePlugin({ name: 'session' }));
app.register(QueuePlugin({ name: 'dead-letter' }));

This escape hatch works because createMicroserviceApp returns an un-started IKernelApplication that accepts additional registrations.

Realtime and DI arms

createMicroserviceApp inherits the realtime and di arms from the REST starter — the option type extends RestStarterOptions, so every sub-arm behaves identically and nothing new is registered by default.

const app = createMicroserviceApp({
  di: {},
  realtime: { sse: {}, websocket: {}, backplane: { transport: 'messaging' } },
});

{ transport: 'messaging' } works on this tier without extra wiring: MessagingPlugin is always registered, so the backplane finds a broker under CAPABILITIES.MESSAGING. On the REST tier that combination throws at start().

See rest-starter for the full description of each arm.

Coming from NestJS

NestJSSetu-TS
@Module({ … })A plugin — IPlugin with provides: [CAPABILITIES.X]
providers: [UserService]decorators: { services: [UserService] }, or app.register(…)
@Injectable()@Injectable({ token, scope })
Constructor injection by type@Inject(token, …) on the class, one entry per argument
@Controller('/users')@Controller('/users') — identical
@Get() / @Post()@Get() / @Post() — identical
@Body() / @Query()@Params(Body(), Query()) — sources, listed in argument order
Guard (CanActivate)@UseGuards(fn), or an auth-plugin guard factory
Pipe (ValidationPipe)@ValidateBody(schema) (Zod)
Interceptor@UseInterceptors(fn)
Exception filter@UseFilters(fn), or errorHandler() middleware
imports: [OtherModule]ctx.services.get(CAPABILITIES.X) — no plugin imports another
DI is requiredDI is the optional di arm

The one difference that will bite you: constructor injection needs an explicit token.

import { Inject, Injectable } from '@setu-ts/decorator-plugin';
import { CAPABILITIES, type ILogger } from '@setu-ts/common';

@Injectable({ token: 'user-service' })
@Inject(CAPABILITIES.LOGGER)
class UserService {
  constructor(private logger: ILogger) {}
}

constructor(private db: DatabaseService) cannot work here. Inferring the token from the parameter’s type requires emitDecoratorMetadata, which Deno does not support — so the type is simply not available at runtime. This is permanent, not a gap waiting to be filled.

Two consequences follow from the list being positional:

  • The Nth entry binds the Nth constructor argument, so reordering the constructor without reordering the tokens misinjects every argument — they are one declaration and move together.
  • A list shorter than the constructor leaves the trailing arguments undefined; a positional list cannot have gaps. Method parameters are separate, and bind with @Params(...).

See Also

Exports

ExportKind
buildMicroservicePluginsfunction
createMicroserviceAppfunction
MicroserviceStarterOptionsinterface
RealtimeArminterface

Generated from the package barrel by pnpm run docs:exports; pnpm run check:docs fails when it drifts.

Agent card · TypeDoc API reference