Deno Deploy integration
Deno Deploy runs JavaScript and TypeScript at the edge in Deno isolates. Because Deno natively resolves npm packages via the npm: specifier, you can use @avsbhq/edge without any build step. By the end of this guide your Deno Deploy function will evaluate feature flags from a KV-cached datafile and flush events after the response is sent.
Import the package
Deno resolves npm packages at runtime using the npm: prefix. No package.json or deno.json install step is required, though you can pin the version in your import map for reproducibility.
1import { AvsbEdgeClient } from 'npm:@avsbhq/edge'Or pin the version in deno.json:
1{2 "imports": {3 "@avsbhq/edge": "npm:@avsbhq/edge@^1.0.0"4 }5}Then import without the npm: prefix:
1import { AvsbEdgeClient } from '@avsbhq/edge'Obtain your SDK key
Open your A vs B project, navigate to Settings → Environments, and copy the Server SDK key. Add it to Deno Deploy as a project environment variable named AVSB_SDK_KEYin your project's settings on dash.deno.com.
Bootstrap the edge client
Deno Deploy isolates do not have persistent background timers between requests. The AvsbEdgeClient reads the datafile from Deno KV on startup and falls back to the A vs B CDN if KV is empty.
1import { AvsbEdgeClient } from 'npm:@avsbhq/edge'2
3// Deno KV — available in all Deno Deploy projects4const kv = await Deno.openKv()5
6function makeKvCache(kv: Deno.Kv) {7 return {8 async get(key: string): Promise<string | null> {9 const result = await kv.get<string>(['avsb', key])10 return result.value ?? null11 },12 async set(key: string, value: string): Promise<void> {13 await kv.set(['avsb', key], value, { expireIn: 3_600_000 })14 },15 }16}17
18const avsb = new AvsbEdgeClient({19 sdkKey: Deno.env.get('AVSB_SDK_KEY')!,20 datafileCache: makeKvCache(kv),21})22
23Deno.serve(async (req: Request): Promise<Response> => {24 await avsb.onReady()25
26 const userId = req.headers.get('x-user-id') ?? 'anon'27 const scoped = avsb.forUser({ kind: 'user', key: userId })28
29 const flag = scoped.getFlag('checkout-v2', false)30
31 // Flush events asynchronously after the response32 // Deno Deploy supports EdgeRuntime.waitUntil in recent versions33 EdgeRuntime.waitUntil(avsb.flush())34
35 return Response.json({ value: flag.value })36})EdgeRuntime.waitUntil is available on Deno Deploy. If you are running locally with deno run for development it may not exist; guard with typeof EdgeRuntime !== 'undefined' or await flush directly before returning the response during local development.Read a flag
1// Boolean flag — T inferred from defaultValue2const flag = scoped.getFlag('checkout-v2', false)3if (flag.value) {4 // serve new checkout experience5}6
7// String flag8const theme = scoped.getFlag('ui-theme', 'default')9
10// JSON flag11const config = scoped.getFlag('pricing-config', { tier: 'standard' })Track an event
1scoped.track('purchase', {2 value: 49.99,3 properties: { currency: 'usd', sku: 'PRO-ANNUAL' },4})Identify a user
Build the EvalContext from the request. Deno Deploy passes Cloudflare geolocation headers so you can include country-based targeting without extra work.
1const scoped = avsb.forUser({2 kind: 'user',3 key: req.headers.get('x-user-id') ?? crypto.randomUUID(),4 plan: req.headers.get('x-user-plan') ?? 'free',5 country: req.headers.get('x-country') ?? 'unknown',6})Keeping the datafile fresh
Register a datafile.published webhook in Settings → Webhooks. Your webhook endpoint should write the updated datafile JSON into Deno KV so all edge instances pick it up on their next request without waiting for the TTL to expire.
1// Minimal — add HMAC signature validation in production2Deno.serve(async (req: Request): Promise<Response> => {3 if (req.method !== 'POST') return new Response('Method Not Allowed', { status: 405 })4 const body = await req.json<{ datafile: string; sdkKey: string }>()5 const kv = await Deno.openKv()6 await kv.set(['avsb', body.sdkKey], body.datafile, { expireIn: 3_600_000 })7 return new Response('ok')8})Graceful shutdown
Deno Deploy isolates are frozen rather than gracefully shut down. Always pass the flush call to EdgeRuntime.waitUntil so it completes after the response is delivered. For local development or when running under deno run with a standard HTTP server, add a Deno.addSignalListener for cleanup:
1Deno.addSignalListener('SIGTERM', async () => {2 await avsb.flush()3 await avsb.close()4 Deno.exit(0)5})Testing
1import { TestData, createMockClient } from 'npm:@avsbhq/test'2import { assertEquals } from 'https://deno.land/std/assert/mod.ts'3
4const td = TestData.flag('checkout-v2')5 .booleanFlag()6 .variationForUser('u_1', true)7 .fallthroughVariation(false)8
9const mock = createMockClient({ flags: [td.build()] })10
11Deno.test('returns true for u_1', () => {12 const flag = mock.forUser({ kind: 'user', key: 'u_1' }).getFlag('checkout-v2', false)13 assertEquals(flag.value, true)14})15
16Deno.test('returns default false', () => {17 const flag = mock.forUser({ kind: 'user', key: 'other' }).getFlag('checkout-v2', false)18 assertEquals(flag.value, false)19})What's next
- Edge SDK reference — full
AvsbEdgeClientAPI. - Cloudflare Workers integration — the same pattern with Cloudflare KV.
- Multi-context targeting — combine user and device contexts in one evaluation.