@bsb/base
v9.0.5
Published
Better Service Base (BSB) is an event-driven microservices framework for Node.js and TypeScript with built-in Zod schema validation, type-safe events, and a pluggable architecture for config, observability (logging, metrics, tracing), and events. It is de
Readme
@bsb/base (Node.js Service Base)
Better Service Base (BSB) is an event-driven microservices framework for Node.js and TypeScript with built-in Zod schema validation, type-safe events, and a pluggable architecture for config, observability (logging, metrics, tracing), and events. It is designed for production-ready, secure-by-default backends with validated APIs and a type-safe event bus.
Version 9.0 introduces breaking changes with improved type safety, cross-language support, and automated code generation. See the Plugin Development Guide for v9 patterns:
https://github.com/BetterCorp/better-service-base/blob/master/nodejs/PLUGIN_DEVELOPMENT.md
Links
- GitHub:
https://github.com/BetterCorp/better-service-base/tree/master/nodejs - BSB Registry (package):
https://io.bsbcode.dev/packages/nodejs/@bsb/base
Intended Usage (Container-first)
- This project is designed to run standalone inside a Docker container and execute plugins authored and published separately.
- It is not intended to be embedded or imported as a library into another application package.
- Deploy the container and supply plugins via
BSB_PLUGIN_DIR(recommended) orBSB_PLUGINSinstallation at container startup.
Requirements
- Node.js >= 23.0.0, npm >= 11.0.0
- TypeScript 5.x for development
Project Structure
src/index.ts: Public exports for the package (base classes, interfaces, controllers).cli.ts: Production CLI entry (also exposed asbin->bsb).dev.ts: Development runner with hot-reload and restart controls.client.ts: Legacy helper for embedding a client; avoid in new code.base/: Core building blocks used by plugins and servicesBSBService,BSBServiceClient: Base classes for service plugins and their clientsPluginObservable,PluginEvents: Per-plugin facades into observability and eventsBSBConfig,BSBObservable,BSBEvents: Base plugin contractsfactory.ts: Option resolution and presets forServiceBase
interfaces/: Strong TypeScript contracts for options, observability, events, results, toolsserviceBase/: Runtime controllers that orchestrate the systemserviceBase.ts: Main runtime (ServiceBase) - boot/init/run/dispose pipelineconfig.ts: Loads and initializes the configuration pluginobservable.ts: Unified logging, metrics, and tracing via observable pluginsevents.ts: Manages event plugins and exposes event APIs (broadcast, emit, return, streams)plugins.ts: Resolves and loads plugins from local build or external locationsservices.ts: Loads, orders, and runs service plugins + their clients
plugins/: Built-in pluginsconfig-default/: Default configuration pluginevents-default/: Default event busobservable-default/: Console-based logging, metrics, and tracingservice-default{0..4}/,service-benchmarkify/: Example/demo service plugins
tests/: Mocha + ts-node test suite
lib/: Compiled JavaScript output (generated bytsc)templates/: Scaffolding for new plugins (plugin.ts,pluginClient.ts,events.ts,logger.ts)Dockerfile,entrypoint.sh,entrypoint.js: Container build/runtime assetstypedoc.json,docs.json,typedoc-theme/: API docs generation configuration/theme
What's New in v9
v9 introduces breaking changes focused on type safety, developer experience, and cross-language support:
Type Safety Improvements:
createEventSchemas()- No moreas constrequired, automatic type inference- Type branding - Compile-time validation that event types match categories
- Duplicate name detection - Warns about confusing duplicate event names
Simplified Configuration:
createConfigSchema()- Single function replaces class pattern- Plugin metadata - Define once, auto-generates PLUGIN_CLIENT and bsb-plugin.json
- Centralized schemas - All generated JSON in lib/schemas/ with JSON $ref references
Cross-Language Support:
- Type helpers - int32, int64, uuid, datetime for precise type mapping
- Schema export - Auto-generates JSON schemas for client code generation
- Multi-language clients - Generate type-safe clients in TypeScript, C#, Go, Java
- Cross-plugin events - Type-safe communication between plugins (no
anytypes)
See Plugin Development Guide for migration details and examples.
Runtime Architecture
The ServiceBase class is the primary entry point. It coordinates the framework subsystems and plugin lifecycle.
Boot flow (high level):
- Construct
ServiceBase(select mode, cwd, and controller implementations) init()sequenceSBConfig.init()-> choose and init configuration pluginSBObservable.init()-> load observable plugins (logging, metrics, tracing)SBEvents.init()-> load events plugins (+ always addsevents-defaultfirst)SBServices.setup()-> discover service plugins from config, create instances, and map dependencies; thenSBServices.init()respecting declared ordering
run()sequence- Start observable, events, then
SBServices.run()(ordered) - Dispose config for safety, start heartbeat metric
- Start observable, events, then
dispose()- Disposes services, events, observable, and config; exits the process
Timekeeping metrics are recorded for each step and logged as timers. A heartbeat counter runs hourly.
Subsystems
SBConfig(configuration)- Defaults to
config-defaultplugin; can be replaced via environment variables - Provides resolved plugin lists: services, events, observable
- Exposes
getPluginConfig()for per-plugin configuration
- Defaults to
SBObservable(observability)- Manages observable plugins for logging, metrics, and tracing
- Routes log, metric, and trace operations via an internal bus with filtering
SBEvents(events)- Loads events plugins and always includes
events-defaultas a fallback - Offers APIs for broadcast, fire-and-forget, request/response, and streaming
- Loads events plugins and always includes
SBServices(services)- Loads service plugins from config, re-maps declared
init/runbefore/after dependencies, and initializes/runs them in order
- Loads service plugins from config, re-maps declared
Plugin Resolution & Layout
SBPlugins looks for plugins in the following order (container usage prefers the first external option):
- Local project (dev):
src/plugins/<type>-<name>/index.ts - Local build:
lib/plugins/<type>-<name>/index.js - External plugin directory (
BSB_PLUGIN_DIR) [preferred in container]:<dir>/<npmPackage>/<major>/<minor>/<micro>/lib/plugins/<type>-<name>/index.js - Node modules:
node_modules/<npmPackage>/lib/plugins/<type>-<name>/index.js
Each plugin folder must export at least a Plugin class. Optionally export a Config class that extends BSBPluginConfig to provide validation and structured config.
Built-in plugin types include: config-*, observable-*, events-*, service-*.
Development vs Production
- Container runtime (production): The container runs
lib/cli.js(bin:bsb) and is the supported production path.- Runs
new ServiceBase(false, true, CWD)(legacy signature -> optimized for production) inside the container entrypoint.
- Runs
- Development runner:
src/dev.ts- Runs
new ServiceBase(true, false, CWD)with file watching - Creates
.bsbdevwatchon first run; supports include/exclude patterns - Interactive controls:
Ctrl+Ror typingrsto restartCtrl+C/Ctrl+Dto dispose and exit
- Runs
NPM Scripts
npm run dev: Start development runner with hot-reloadnpm start: Run production CLI (lib/cli.jsorbsb)npm run tsc: Clean and compile TypeScript tolib/npm run build: Clean -> tsc -> tests -> generate docs -> export schemas -> generate plugin metadatanpm run build-release: Compile usingtsconfig-release.jsonnpm run lint: ESLint oversrc/npm test: Mocha + NYC coverage in TS modenpm run testDev: Run tests without coverage (faster for development)npm run generate-docs: Generate TypeDoc JSON todocs.json- API Reference: Hosted at
https://types.bsbcode.dev/nodejs/ npm run export-schemas: Export event schemas tolib/schemas/{plugin-name}.jsonnpm run generate-plugin-json: Generate plugin metadata inlib/schemas/
Docker
Multi-stage build produces a minimal runtime image:
ENV BSB_LIVE=true,ENV BSB_CONTAINER=true,ENV BSB_PLUGIN_DIR=/mnt/plugins- Volumes:
/mnt/plugins(external plugins),/mnt/temp - Entrypoint runs
node lib/cli.jsas an unprivilegednodeuser - Optional plugin install/update at startup:
BSB_PLUGINS="@scope/plugin-a:1.2.3,@scope/plugin-b"-> installs/updates listed packagesBSB_PLUGIN_UPDATE=yes-> runsnpm update
Example run (with mounted plugins directory):
docker run --rm \
-e BSB_PLUGINS="@bettercorp/your-plugin" \
-v $(pwd)/plugins:/mnt/plugins \
betterweb/service-base:9Recommended plugin directory layout (when using BSB_PLUGIN_DIR):
/mnt/plugins/
@org/plugin-a/
1/2/3/
package.json
lib/plugins/service-plugin-a/index.js
lib/plugins/observable-xyz/index.js
@org/plugin-b/
2/4/1/
package.json
lib/plugins/events-abc/index.jsNotes
- In container deployments, prefer placing prebuilt plugins under
BSB_PLUGIN_DIRas above. This avoids network installs on boot and ensures deterministic versions via immutable versioned folders. BSB_PLUGINSis available for dynamicnpm installat startup, but mounting a curated plugin repository viaBSB_PLUGIN_DIRis recommended for production.
Environment Variables
APP_DIR: Override working directory (mainly used in local development/testing)BSB_PLUGIN_DIR: External plugin repository root (e.g., container volume/mnt/plugins)BSB_PLUGINS: Comma-separated list of npm packages to install at container start (entrypoint.js)BSB_PLUGIN_UPDATE:yes|y|trueto runnpm updateat container start- Config plugin override (advanced):
BSB_CONFIG_PLUGIN: Name of config plugin (must start withconfig-)BSB_CONFIG_PLUGIN_PACKAGE: npm package name hosting the config plugin
Documentation
Plugin Development (v9)
- Plugin Development Guide - Complete guide for creating BSB plugins
- Type System Guide - Cross-language type system reference
API Documentation
- API docs are generated with TypeDoc (
typedoc.json).npm run generate-docs-> emitsdocs.json- API docs are served at
https://types.bsbcode.dev/nodejs/
Testing
- Tests: Mocha + ts-node with NYC coverage
npm test-> CI-style JSON + lcov reports (coverage/)npm run testDev-> dev-friendly TS execution
Creating Plugins
For v9 plugin development, see the Plugin Development Guide for complete examples and best practices.
Quick reference:
- Use
createEventSchemas()to define typed events with compile-time validation - Use
createConfigSchema()to define plugin configuration with metadata - Use cross-language type helpers (
uuid,int32,datetime, etc.) for better code generation - Plugin metadata auto-generates
PLUGIN_CLIENTand schema files during build
At minimum, export a Plugin class in lib/plugins/<type>-<name>/index.js (or src/plugins/.../index.ts in dev). For configurable plugins, export a Config created with createConfigSchema(). Publish your plugin as an npm package or ship its prebuilt folder structure under BSB_PLUGIN_DIR.
Quick Start (Container)
# Provide prebuilt plugins under ./plugins, matching the recommended layout
docker run --rm \
-v $(pwd)/plugins:/mnt/plugins:ro \
-e BSB_PLUGIN_DIR=/mnt/plugins \
betterweb/service-base:9Local development (for contributors only):
npm install
npm run dev