Active npm 1.0.3 @soasap-com/angular-sdk

SOASAP Angular SDK

Lightweight, production-ready feature flags SDK for Angular 17+ — O(1) local evaluation, local-first browser caching, real-time SSE updates, reactive Signals, zero runtime dependencies, and graceful offline behavior. Standalone components and bootstrapApplication.

Installation

Install from npm. Requires @angular/core 17+ as a peer dependency. Supports Angular 17, 18, and 19+.

npm install @soasap-com/angular-sdk

Quick start

Register with provideSoasap() at bootstrap, enable preload: true for non-blocking startup sync, and inject SoasapService to read reactive Signals in components.

// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { provideSoasap } from '@soasap-com/angular-sdk';
import { AppComponent } from './app/app.component';

bootstrapApplication(AppComponent, {
  providers: [
    provideSoasap({
      apiKey: environment.soasapApiKey,
      preload: true,
    }),
  ],
});

// checkout.component.ts
import { Component, inject, type Signal } from '@angular/core';
import { SoasapService } from '@soasap-com/angular-sdk';

@Component({
  selector: 'app-checkout',
  template: `
    @if (newCheckout()) {
      <app-new-checkout />
    } @else {
      <app-legacy-checkout />
    }
  `,
})
export class CheckoutComponent {
  private readonly soasap = inject(SoasapService);
  readonly newCheckout: Signal<boolean> = this.soasap.bool('new-checkout');
}

Imperative client (no Signals)

Use createSoasapClient() for sync reads outside Angular components — guards, interceptors, or non-UI code paths.

import { createSoasapClient } from '@soasap-com/angular-sdk';

const flags = createSoasapClient({
  apiKey: environment.soasapApiKey,
  preload: true,
});

if (flags.getBool('new-checkout')) {
  console.log('New checkout enabled');
}

Typed access

SoasapService methods return Angular Signals — read them in templates with () (e.g. featureX()). JSON keys from the dashboard are returned as stored (typically camelCase).

// Signals (reactive)
this.soasap.bool('feature-x');
this.soasap.number('rate-limit', 100);
this.soasap.string('ui-theme', 'light');
this.soasap.json<CheckoutConfig>('checkout-config');

// Client (sync)
flags.getBool('feature-x');
flags.getNumber('rate-limit', 100);
flags.getString('ui-theme', 'light');
flags.getJson<CheckoutConfig>('checkout-config');

Startup sync

preload: true (recommended) starts the SSE worker in the background, loads the browser cache on cold start, and never blocks Angular bootstrap. Without it, lazy mode defers the network connection until the first flag read.

In lazy mode, the first evaluation uses default values (or cached flags from localStorage if available) while SSE connects in the background.

// Immediate sync (recommended)
provideSoasap({ apiKey: '...', preload: true })

// Lazy sync — first read falls back to defaults or storage cache
provideSoasap({ apiKey: '...' })

Shared client instance

Create one client per browser tab and pass it to provideSoasap(client). Pre-built clients are not closed automatically — call close() yourself.

import { createSoasapClient, provideSoasap } from '@soasap-com/angular-sdk';

const flags = createSoasapClient({
  apiKey: environment.soasapApiKey,
  preload: true,
});

bootstrapApplication(AppComponent, {
  providers: [provideSoasap(flags)],
});

Error handling & observability

Hook background diagnostics without affecting the hot path. Sources: Network, Storage, Parser.

import { SoasapErrorSource, provideSoasap } from '@soasap-com/angular-sdk';

provideSoasap({
  apiKey: '...',
  onError: (ctx) => {
    console.error(
      `[${ctx.source}] transient=${ctx.isTransient}`,
      ctx.exception.message);
  },
});

// SoasapErrorSource.Network | .Storage | .Parser

Production safety & guardrails

Getters and signals never throw (except missing provider when injecting SoasapService).

Offline resiliency

ScenarioBehavior
API unavailableUses stale cached flags
SSE disconnectedKeeps last known snapshot
First visit without cacheReturns default values
Invalid payloadPayload ignored
Storage quota / failureIn-memory mode continues
Persistent network issuesAutomatic reconnect with backoff

Browser storage key: soasap:cache:<api-key-prefix>.
Override with cacheKeyPrefix: 'myapp:flags'.

Graceful shutdown

provideSoasap({ apiKey, ... }) calls close() automatically when the injector is destroyed. Pre-built clients passed to provideSoasap(client) are not closed automatically — call await flags.close() when done.

Supported environments

← All SDKs