@soasap-com/node-sdk
SOASAP Node.js SDK
Lightweight, production-ready feature flags SDK for Node.js 18+ — lock-free O(1) reads, zero network calls on startup, real-time SSE updates, persistent disk cache, zero runtime dependencies, and graceful offline behavior. Express, Fastify, serverless.
Installation
Install from npm. Supports Node.js 18, 20, and 22+. ESM and CommonJS entry points included.
npm install @soasap-com/node-sdk
Quick start
Create a client with createSoasapClient(), enable
preload: true for non-blocking startup sync,
and read flags synchronously anywhere in your process.
import { createSoasapClient } from '@soasap-com/node-sdk';
const flags = createSoasapClient({
apiKey: process.env.SOASAP_API_KEY!,
preload: true,
});
// Sync reads — never throw, never hit the network
if (flags.getBool('new-checkout')) {
console.log('New checkout enabled');
}
Use feature flags
Evaluate flags on the hot path with zero network I/O. Getters never throw — safe for production request handlers.
app.get('/', (req, res) => {
if (flags.getBool('maintenance-mode')) {
res.status(503).send('Maintenance');
return;
}
res.send('OK');
});
Typed access
Bool, number, string, and JSON remote config.
JSON keys from the dashboard are returned as stored (typically camelCase).
const enabled = flags.getBool('feature-x');
const limit = flags.getNumber('rate-limit', 100);
const theme = flags.getString('ui-theme', 'light');
const config = flags.getJson<CheckoutConfig>('checkout-config');
interface CheckoutConfig {
enableUpsells: boolean;
maxItems: number;
}
Startup sync
preload: true (recommended) starts the SSE worker in the background,
loads the disk cache on cold start, and never blocks module initialization or server boot.
Without it, lazy mode defers the network connection until the first flag read.
In lazy mode, the first evaluation uses default values (or the local disk cache if available) while the SSE stream connects in the background.
// Immediate sync (recommended)
const flags = createSoasapClient({
apiKey: process.env.SOASAP_API_KEY!,
preload: true,
});
// Lazy sync — first read falls back to defaults or disk cache
const flags = createSoasapClient({
apiKey: process.env.SOASAP_API_KEY!,
});
Express integration
Attach one client instance per process via middleware.
Use flags.close() on shutdown to flush the disk cache.
import express from 'express';
import { createSoasapClient } from '@soasap-com/node-sdk';
const flags = createSoasapClient({
apiKey: process.env.SOASAP_API_KEY!,
preload: true,
});
const app = express();
app.use((req, _res, next) => {
(req as express.Request & { soasap: typeof flags }).soasap = flags;
next();
});
process.on('SIGTERM', async () => {
await flags.close();
process.exit(0);
});
Error handling & observability
Hook background diagnostics without affecting the hot path.
Sources: Network, Disk, Parser.
import { SoasapErrorSource, createSoasapClient } from '@soasap-com/node-sdk';
createSoasapClient({
apiKey: '...',
onError: (ctx) => {
console.error(
`[${ctx.source}] transient=${ctx.isTransient}`,
ctx.exception.message);
},
});
// SoasapErrorSource.Network | .Disk | .Parser
Production safety & guardrails
- Immutable snapshots — atomic reference swap; readers never see partial updates
- Memory cap protection (anti-DoS) — 5 MB SSE payload cap; oversized streams are dropped and reset
- Payload validation — root element must be a JSON object
{}; invalid payloads are ignored - IO coalescing (disk debounce) — disk writes coalesced at most once every ~2.5 seconds
- Zero runtime dependencies — no transitive npm packages in production
- One client per process — do not share across
worker_threadswithout IPC
The SDK never throws from flag getters.
Offline resiliency
| Scenario | Behavior |
|---|---|
| API unavailable | Uses stale cached flags |
| SSE disconnected | Keeps last known snapshot |
| First startup without cache | Returns default values |
| Invalid payload | Payload ignored |
| Disk cache failure | In-memory mode continues |
| Persistent network issues | Automatic reconnect with backoff |
Disk cache: %LOCALAPPDATA%\soasap\cache (Windows),
~/.local/share/soasap/cache (Linux/macOS).
Override with cacheDirectory: '/custom/path'.
Architecture
[Hot Path] getBool() → currentSnapshot ref → O(1) lookup
↑
| (atomic reference swap)
[Background] SSE → SseEventParser (5MB cap) → DiskWriteCoalescer → disk
Supported runtimes
- Node.js 18 / 20 / 22+
- ESM (
import) and CommonJS (require) - TypeScript types included