Bun integration
Bun is a fast all-in-one JavaScript runtime that implements both the Node.js API surface and the Web Fetch API. You can use either @avsbhq/node (for the full server SDK with background polling, sticky bucketing, and decision logging) or @avsbhq/edge (for the lighter, fetch-only client). This guide covers both and shows how to run a hot-reloading development server with bun --hot.
Using @avsbhq/node (recommended for long-running servers)
Install
1bun add @avsbhq/node @avsbhq/utilsObtain your SDK key
Go to Settings → Environments in your A vs B project and copy the Server SDK key. Store it in a .env file or your deployment environment — never in source code.
1AVSB_SDK_KEY=sdk_production_...Bootstrap the server
Initialise AvsbServerat module scope so it is shared across all requests in the same process. Bun's module cache ensures the singleton is reused even with --hot reloading.
1import { AvsbServer } from '@avsbhq/node'2
3const server = new AvsbServer({ sdkKey: process.env.AVSB_SDK_KEY! })4
5// Wait for the first datafile fetch before accepting traffic6await server.onReady()7
8Bun.serve({9 port: 3000,10 async fetch(req: Request): Promise<Response> {11 const userId = req.headers.get('x-user-id') ?? 'anon'12 const plan = req.headers.get('x-user-plan') ?? 'free'13
14 const avsb = server.forUser({ kind: 'user', key: userId, plan })15
16 const flag = avsb.getFlag('checkout-v2', false)17
18 return Response.json({ value: flag.value })19 },20})Run the dev server with hot reload
1bun --hot run server.ts--hot, Bun re-imports modules on file change but keeps the same process alive. Because AvsbServer is declared at module scope, it is re-initialised on each hot reload cycle. For development this is fine; in production you would run without --hot.Read a flag
1// Boolean — T inferred as boolean2const flag = avsb.getFlag('checkout-v2', false)3
4// String5const theme = avsb.getFlag('ui-theme', 'default')6
7// JSON8const pricing = avsb.getFlag('pricing-config', { tier: 'standard', seats: 1 })9
10// Access the full Flag<T> object11console.log(flag.source, flag.variationKey, flag.reasons)Track an event
1avsb.track('purchase', {2 value: 99.00,3 properties: { sku: 'PRO-ANNUAL', currency: 'usd' },4})Identify a user
For multi-context evaluations — for example when you also want to target by organisation or device — build a multi-context object:
1const avsb = server.forUser({2 kind: 'multi',3 user: { kind: 'user', key: userId, plan },4 organization: { kind: 'organization', key: orgId, tier: 'enterprise' },5})Using @avsbhq/edge (lighter option)
If you want the smallest possible footprint — for example in a Bun edge-proxy or serverless-style handler — use @avsbhq/edge instead. It has no background timers and loads the datafile once per process.
1import { AvsbEdgeClient } from '@avsbhq/edge'2
3const avsb = new AvsbEdgeClient({ sdkKey: process.env.AVSB_SDK_KEY! })4await avsb.onReady()5
6Bun.serve({7 port: 3000,8 async fetch(req: Request): Promise<Response> {9 const userId = req.headers.get('x-user-id') ?? 'anon'10 const scoped = avsb.forUser({ kind: 'user', key: userId })11 const flag = scoped.getFlag('new-banner', false)12 return Response.json({ value: flag.value })13 },14})Graceful shutdown
Bun propagates SIGTERM to the process. Register a handler to flush pending events before exiting:
1process.on('SIGTERM', async () => {2 await server.flush()3 await server.close()4 process.exit(0)5})Testing
Bun ships its own test runner compatible with Jest matchers. Use @avsbhq/test to create mock clients without network access.
1import { TestData, createMockClient } from '@avsbhq/test'2import { describe, it, expect } from 'bun:test'3
4const td = TestData.flag('checkout-v2')5 .booleanFlag()6 .variationForUser('u_pro', true)7 .fallthroughVariation(false)8
9const mock = createMockClient({ flags: [td.build()] })10
11describe('flag evaluation', () => {12 it('returns true for pro user', () => {13 const flag = mock.forUser({ kind: 'user', key: 'u_pro' }).getFlag('checkout-v2', false)14 expect(flag.value).toBe(true)15 })16
17 it('returns false as fallthrough', () => {18 const flag = mock.forUser({ kind: 'user', key: 'other' }).getFlag('checkout-v2', false)19 expect(flag.value).toBe(false)20 })21})What's next
- Server SDK reference — full
AvsbServerAPI including decision logging and sticky bucketing. - Edge SDK reference — full
AvsbEdgeClientAPI. - Sticky bucketing — guarantee consistent variation assignment across restarts.
- AWS Lambda integration — similar pattern for short-lived function invocations.