Skip to main content

Koa (@ops-ai/toggly-koa)

Examples and requirements​

Start with the Koa SDK sample. Its README explains the setup, then src/catalog.js names the flags and filter inputs, src/app.js shows request-bound evaluation and Order checks, and test/http.test.js demonstrates the expected enabled, disabled, and isolated responses. Use the Samples catalog to find the other runnable SDK examples.

Identity, groups, and claims are targeting inputs, not authentication. Populate them from your trusted session; a caller-controlled identity header does not authorize a user.

Requires Node.js 18+ and supports Koa 2.x or 3.x (^2.0.0 || ^3.0.0).

Install​

npm install @ops-ai/toggly-koa

Middleware​

import Koa from 'koa'
import Router from '@koa/router'
import { togglyMiddleware, featureGate, closeKoaToggly } from '@ops-ai/toggly-koa'

const app = new Koa()
const router = new Router()

app.use(
togglyMiddleware({
appKey: process.env.TOGGLY_APP_KEY!,
environment: 'Production',
verifySignatures: true,
enableStreaming: true,
}),
)

router.get('/dashboard', async (ctx) => {
if (await ctx.state.toggly.isFeatureOn('new-dashboard')) {
ctx.body = { version: 'v2' }
return
}
ctx.body = { version: 'v1' }
})

router.get(
'/beta',
featureGate({ featureKey: 'beta-access' }),
async (ctx) => {
ctx.body = { ok: true }
},
)

app.use(router.routes())

process.on('SIGTERM', () => closeKoaToggly())

Helpers live on ctx.state.toggly.

Configure identity, groups, and claims once on middleware. Segment request.* fields are filled from headers via fromHttpRequest. Then ctx.state.toggly.isFeatureOn('X') needs no context argument.

app.use(
togglyMiddleware({
appKey: process.env.TOGGLY_APP_KEY!,
getIdentity: (ctx) => ctx.state.user?.id,
getGroups: (ctx) => ctx.state.user?.roles ?? [],
getClaims: (ctx) => ({ role: ctx.state.user?.role ?? '' }),
// Or getContext: (ctx) => ({ identity, groups, claims, request })
}),
)

router.get('/checkout', async (ctx) => {
if (await ctx.state.toggly.isFeatureOn('MobileCheckout')) {
ctx.body = { flow: 'mobile' }
return
}
ctx.body = { flow: 'desktop' }
})

Default identity (when getIdentity is unset): x-toggly-identity header.

When you supply getContext, missing request keys are still filled from headers. Prefer getIdentity / getGroups / getClaims for the happy path.

Overrides and entity checks​

In the adapter, the request helper isFeatureOn(key) accepts only the flag key and uses the middleware's context. Passing a second argument does not override that context. Call its core client explicitly and merge the fields you want to preserve:

const toggly = ctx.state.toggly
const evaluation = {
...toggly.context,
identity: 'impersonated-user',
claims: { ...toggly.context.claims, role: 'admin' },
}
await toggly.client.isFeatureOn('MobileCheckout', evaluation)

// A ready entity context keeps Order data separate from user targeting.
await toggly.client.isFeatureOn('OrderBadge', toggly.context, {
kind: 'Order',
key: 'ORD-42',
attributes: { Status: 'Paid' },
})

The entity kind/property must match the feature definition. See Node entity context for mapper registration and HTTP segment filters for request fields.

Feature gates​

Same gate options as Express. Also: featureRoutes, withFeature, featuresHandler(), getKoaToggly.

Core config: Configuration.