JavaScript client
Package: nona-client
OpenFeature provider packages: nona-openfeature-provider (server), nona-openfeature-web-provider (browser)
Requirements:
- Node.js 18 or newer, or a runtime with
fetch,Headers, andResponse - ESM imports
The JavaScript client is a good fit for:
- Node.js services
- server-side JavaScript applications
- React Native and similar environments that can use
fetch - teams that want a lighter integration than OpenFeature but more convenience than raw HTTP
Install
Section titled “Install”npm install nona-clientPrepare the value in admin
Section titled “Prepare the value in admin”Before writing app code:
- open
Projects - open the project the app belongs to
- select the target environment such as
production - create the parameter or flag you want to read
- create an API key in the
API Keyssection - choose
clientscope for React Native or other app-side reads
For a first test, create a boolean parameter such as Features:Checkout.
Prepare the value with the CLI
Section titled “Prepare the value with the CLI”nona entries set \ --project storefront \ --environment production \ --key Features:Checkout \ --value true \ --scope client \ --content-type boolean
nona keys create \ --project storefront \ --name "React Native app" \ --scope client \ --environment productionThe default client reads working parameters, so no release is required for this example. Publish a release only when you enable release reads; set it active if you omit releaseVersion.
Read a string
Section titled “Read a string”import { createNonaClient } from "nona-client";
const nona = createNonaClient({ baseUrl: "https://nona.example.com", environmentId: "production", apiKey: process.env.NONA_API_KEY});
const checkout = await nona.getStringValue("Features:Checkout");
const checkoutEnabled = checkout === "true";For actual feature flags, it is usually better to keep the entry typed as boolean, then inspect the metadata or use OpenFeature if you want a flag-oriented interface.
Read a boolean flag cleanly
Section titled “Read a boolean flag cleanly”const checkout = await nona.getConfigValue("Features:Checkout");
const checkoutEnabled = checkout.contentType === "boolean" && checkout.value === "true";That keeps the application aligned with Nona’s real content type instead of treating every flag as a plain string.
Read value metadata
Section titled “Read value metadata”const value = await nona.getConfigValue("Features:Checkout");
console.log(value.value);console.log(value.contentType);contentType is one of text, number, boolean, or json.
This is useful when one application needs to inspect the logical type before deciding how to handle the value.
Read JSON
Section titled “Read JSON”const settings = await nona.getJsonValue("App:Settings");Example setup in Nona:
- key:
App:Settings - content type:
json - scope:
client
Use JSON when related settings belong together and your application naturally consumes them as one object.
Return null for missing keys
Section titled “Return null for missing keys”const value = await nona.tryGetConfigValue("Missing:Key");
if (value === null) { console.log("Key was not found");}This is helpful for optional settings or cases where a key may not exist in every environment yet.
Fetch once and prime the cache
Section titled “Fetch once and prime the cache”Fetch every client-visible value at startup with one HTTP request:
const values = await nona.getAllValues();
const checkout = await nona.tryGetConfigValue("Features:Checkout");const banner = await nona.tryGetConfigValue("App:Banner");getAllValues() returns a map of { key: { value, contentType } } and primes the in-memory snapshot, so subsequent reads of those keys are local even when the optional TTL cache is disabled. A six-flag startup therefore makes one request instead of six.
The server response contains only entries visible to clients: client and all entries are included, while server-only entries are excluded. Use a client or all API key.
Pass a prefix to load one key group:
const features = await nona.getAllValues({ prefix: "Features:" });Prefixes may contain ASCII letters, digits, colons, dots, underscores, and dashes, and matching is case-insensitive. Empty or omitted prefixes fetch the complete snapshot. Any other character produces NonaClientError with status === 400; failed responses are not cached. Each normalized prefix has an independent ETag snapshot and in-flight request identity, so results from different groups cannot be mixed. Only keys returned by the request are primed for later single-key reads.
Call getAllValues() again to poll for changes. The client automatically sends the previous ETag; when the server returns 304 Not Modified, the existing snapshot is reused without downloading the JSON again.
Pin a release version
Section titled “Pin a release version”By default, reads use working parameters. Set useReleases when constructing the client to read immutable release snapshots instead.
Pin a client to an exact release or release line with releaseVersion:
const nona = createNonaClient({ baseUrl: "https://nona.example.com", environmentId: "production", apiKey: process.env.NONA_API_KEY, useReleases: true, releaseVersion: "1.1.x"});Use an exact version such as 1.1.0 for a fixed snapshot. Use a line such as 1.1.x to read the highest patch in that line.
Omit releaseVersion while keeping useReleases: true to follow the environment’s active release. If no active release exists, reads fail with 409 and errorCode === "active_release_not_configured"; they never fall back to working parameters. Source and selector are fixed for the client’s lifetime, so create another client for a different source or selector. When useReleases is false (the default), releaseVersion is normalized and retained but ignored for requests and cache identity.
Handle HTTP errors
Section titled “Handle HTTP errors”import { createNonaClient, NonaClientError } from "nona-client";
const nona = createNonaClient({ baseUrl: "https://nona.example.com", environmentId: "production", apiKey: process.env.NONA_API_KEY});
try { await nona.getConfigValue("Missing:Key");} catch (error) { if (error instanceof NonaClientError) { console.error(error.status); console.error(error.errorCode); console.error(error.detail); console.error(error.message); throw error; }
throw error;}When to use the JavaScript client
Section titled “When to use the JavaScript client”Use the JavaScript client when you want:
- a straightforward Nona-specific API
- runtime reads in JavaScript or TypeScript
- optional in-memory caching
- a smaller abstraction layer than OpenFeature
Use HTTP instead when the app only needs one very small direct read path.
Optional cache
Section titled “Optional cache”const nona = createNonaClient({ baseUrl: "https://nona.example.com", environmentId: "production", apiKey: process.env.NONA_API_KEY, cacheTtlMs: 30_000, cacheMemoryLimitMegabytes: 5});Use invalidateTtlCache(key) to remove one cached value or clearTtlCache() to clear all cached values, including a snapshot primed by getAllValues().
The JavaScript client cache is optional and disabled by default. Set a positive cacheTtlMs value to enable it.
The cacheMemoryLimitMegabytes budget is shared by TTL entries and bulk snapshots; least-recently-used data is evicted when the total exceeds that limit.
Cache is useful when:
- the same keys are read repeatedly
- you want to reduce request volume
- the application can tolerate slightly older values for a short TTL
Keep the TTL short for operational flags and kill switches unless you are sure longer cache windows are acceptable.
Basic troubleshooting
Section titled “Basic troubleshooting”If a JavaScript read fails:
- confirm
environmentIdmatches the environment name in Nona - confirm that
useReleasesselects the intended source - in release mode, confirm the expected release is active or configure
releaseVersion - confirm the API key belongs to the same project as the parameter
- confirm the parameter scope is readable by that key
- try the same key once with HTTP to isolate client-code issues
Good first app flow
Section titled “Good first app flow”For a mobile or JavaScript app, the usual sequence is:
- call
getAllValues()once to prime startup flags - confirm the value changes when you edit it in admin
- add TTL cache only if repeated reads justify it
- move to OpenFeature when the app becomes flag-heavy
OpenFeature provider
Section titled “OpenFeature provider”Install the optional provider package alongside the Nona client and OpenFeature server SDK:
npm install nona-client nona-openfeature-provider @openfeature/server-sdkimport { OpenFeature } from "@openfeature/server-sdk";import { createNonaOpenFeatureProvider } from "nona-openfeature-provider";
const domain = "nona-production";
await OpenFeature.setProviderAndWait(domain, createNonaOpenFeatureProvider({ baseUrl: "https://nona.example.com", apiKey: process.env.NONA_API_KEY, environmentId: "production"}));
const client = OpenFeature.getClient(domain);const enabled = await client.getBooleanValue("Features:Checkout", false);In the browser, use nona-openfeature-web-provider with @openfeature/web-sdk instead — OpenFeature’s browser paradigm evaluates synchronously from a preloaded snapshot, and needs a frontend-scoped API key:
npm install nona-client nona-openfeature-web-provider @openfeature/web-sdkIf your team thinks in terms of feature flags more than direct config reads, see OpenFeature.