Node.js
This SDK can be used both on the server and in the browser. If you are using a frontend framework like React or Vue you probably want to use the specialized SDK which uses this package under the hood.
Installation
Add the client to your dependencies:
npm i tggl-client
Quick start
There are two ways to evaluate flags: stateful and stateless.
Stateful flags evaluation stores result in the client itself.
This is commonly used when instantiating a new client per HTTP request or when working on the frontend.
You can use get
on the client to access results:
import { TgglClient } from 'tggl-client'
const client = new TgglClient('YOUR_API_KEY')
await client.setContext({
userId: 'foo',
email: 'foo@gmail.com',
country: 'FR',
// ...
})
if (client.get('my-feature', 'Variation A') === 'Variation A') {
// ...
}
Stateless flags evaluation does not change the client state.
It is commonly used on the backend with a global singleton client.
You can use get
on the response to access results:
const flags = await client.evalContext({
userId: 'foo',
email: 'foo@gmail.com',
country: 'FR',
// ...
})
if (flags.get('my-feature', 'Variation A') === 'Variation A') {
// ...
}
Hard-coded fallback values
You must provide a fallback value that will be returned if the flag is inactive, does not exist, or in case of network error:
if (client.get('my-feature', 'Variation A') === 'Variation A') {
// This code will be executed if 'my-feature' is either:
// - explicitly equal to 'Variation A'
// - deleted or network error
}
Typing
CLI
Using the Tggl CLI you can run an introspection query to generate the TypeScript types for your flags and context.
# Install the CLI once
npm i --save-dev tggl-cli
# Generate the types every time that it is needed
tggl typing -k <SERVER_API_KEY> -o src/tggl.d.ts
# Drop the -k option if you have the TGGL_API_KEY environment variable set
tggl typing -o src/tggl.d.ts
Replace <SERVER_API_KEY>
with your server API key or use the TGGL_API_KEY
environment variable and omit the -k
option. You should run this command everytime you need to update the typing. Your IDE will now autocomplete and type-check the context properties and all flag names and values.
All context properties are required except properties that you have hidden. You can also use the -h
option to remove hidden properties from the context.
Option | Description |
---|---|
-k, --api-key <key> | Tggl API key, defaults to TGGL_API_KEY environment variable |
-o, --output <file> | File to write the typing to, *.d.ts to override the package typing, *.ts to simply generate interfaces |
-h, --skip-hidden | Skip hidden properties in context (default: false) |
-p, --package <package> | Name of the package to declare types for (default: "tggl-client") |
--help | display help for command |

Typing system
The CLI generate two interfaces: TgglContext
and TgglFlags
that look like this (based on your own configuration on Tggl):
interface TgglContext {
userId: string
email: string
timestamp: string | number
environement: "production" | "local" | "staging"
}
interface TgglFlags {
new_blog_layout: true
color_button: "#00ff00" | "#0000ff"
}
They are used by default by the client, but you can manually override them if you need to, notably if you want to instantiate multiple clients for different projects:
const clientOne = new TgglClient<
FlagsProjectOne,
ContextProjectOne
>('API_KEY_ONE')
const clientTwo = new TgglClient<
FlagsProjectTwo,
ContextProjectTwo
>('API_KEY_TWO')
The SDK also exports some helper types if needed:
import { TgglFlagSlug, TgglFlagValue, TgglFlags } from 'tggl-client'
// Slug type
const slug: TgglFlagSlug = 'new_blog_layout'
const slug: TgglFlagSlug<{ flag_a: true }> = 'flag_a'
// Value type
const value: TgglFlagValue<'color_button'> = "#00ff00"
const value: TgglFlagValue<'my_flag', { my_flag: 'a' | 'b'}> = "b"
// Use it to build typed functions
function getFlag<
TFlags extends TgglFlags = TgglFlags,
TSlug extends TgglFlagSlug<TFlags> = TgglFlagSlug<TFlags>
>(slug: TSlug): TgglFlagValue<TSlug, TFlags> | undefined {
// ...
}
Network performance
A single API call evaluating all flags is performed when calling
setContext
or evalContext
, making all subsequent flag checking methods synchronous and extremely fast.
This means that you do not need to cache results of get()
since
it do not trigger an API call, it simply look up the data in the already fetched response.
Evaluating contexts in batches
If you have multiple contexts to evaluate at once, you can batch your calls in a single HTTP request for a significant performance boost:
// Responses are returned in the same order
const [ fooFlags, barFlags ] = await client.evalContexts([
{ userId: 'foo' },
{ userId: 'bar' },
])
The client uses a dataloader under the hood, which means that all calls that are performed within the same event loop are batched together:
// evalContext is called twice but a single API call is performed
const [ fooFlags, barFlags ] = await Promise.all([
client.evalContext({ userId: 'foo' }),
client.evalContext({ userId: 'bar' }),
])
Reporting
By default, the SDK sends flag evaluation data to Tggl to give you precious insights on how you are actually using your flags in production and to help you manage flags lifecycles better.

The SDK sends at most one HTTP request every 2 seconds to Tggl. Additionally, you can identify the client making the request by giving it an app name. You will be able to retrieve that name on the Tggl dashboard:
const client = new TgglClient('API_KEY', {
reporting: {
app: 'My App:2.16.3'
}
})
You can also disable reporting entirely:
// Disable reporting when you instantiate the client
const client = new TgglClient('API_KEY', { reporting: false })
// Or disable it later
const client = new TgglClient('API_KEY')
client.disableReporting()
// Or disable it only for one response
const client = new TgglClient('API_KEY')
const flags = client.evalContext({ userId: 'foo' })
flags.disableReporting()
Polling for "live" updates
If you need your client to be up-to-date with the latest flag configuration, you can manually call setContext
or evalContext
to make another API call, or simply enable polling to automatically make an API call at regular intervals:
// Update flags every 5 seconds
const client = new TgglClient('YOUR_API_KEY', {
pollingInterval: 5000
})
Polling will only make an API call if the previous call has finished, so you don't have to worry about overlapping calls. You can also manually start and stop polling at anytime:
client.startPolling(8000) // Start polling every 8 seconds
client.startPolling(3000) // Change frequency to every 3 seconds
client.stopPolling() // Stop polling
Polling will update the internal state of the client, you can add listeners to be notified when the state changes:
const stopListener = client.onResultChange((flags) => {
// Some flag has changed
// Either check the flags object or use `.get()` on the client
})
// Call stopListener() to remove the listener
onResultChange
is not specific to polling, it is also called when calling setContext
manually.
Using the Proxy
By default, the SDK talks directly to the Tggl API. If you are using the Tggl Proxy, you can specify the proxy URL when instantiating the client:
const client = new TgglClient('YOUR_API_KEY', {
baseUrl: 'http://your-proxy-domain.com'
})
The /flags
and /report
path will be appended to the baseUrl
and both flags evaluation and reporting will go through the proxy. If your proxy is configured with custom paths, you can specify them:
const client = new TgglClient('YOUR_API_KEY', {
url: 'http://your-proxy-domain.com/custom-flags',
reporting: {
url: 'http://your-proxy-domain.com/custom-report'
}
})
Error handling
Calling setContext
will never throw an error even if the API call is not successful. Instead, the client internal state will not change, leaving all flags as-is.
This is also true for evalContext
, it will return a response object with all flags disabled.
This behavior ensures that your app will never crash because of a flag evaluation, even if the API is down or if you have a typo in your API key. You can listen for success and errors to handle them as you wish:
client.onFetchSuccessful(() => { /* ... */ })
client.onFetchFail((error) => { /* ... */ })
Evaluating flags locally
It is possible to evaluate flags locally on the server but not recommended unless you have performance issues evaluating flags at a high frequency, or if you need to split traffic on the edge without doing an API call. Evaluating flags locally forces you to maintain the copy of flags configuration up to date and might be a source of issues.
Make sure to add the right keys to your context to be perfectly consistent with the Tggl API.
import { TgglLocalClient } from 'tggl-client'
const client = new TgglLocalClient('YOUR_SERVER_API_KEY')
// This method performs an API call and updates the flags configuration
await client.fetchConfig()
// Evaluation is performed locally
client.get({ userId: 'foobar' }, 'my-feature', 42)
When evaluating flags locally it is your responsibility to keep the configuration up to date by calling fetchConfig
when needed. You can use webhooks to be notified when the configuration changes or simply poll the API at regular intervals:
const client = new TgglLocalClient('YOUR_SERVER_API_KEY', {
pollingInterval: 5000
})
You can cache the configuration and instantiate the client with the cached version, so you don't need to call fetchConfig
:
import { TgglLocalClient } from 'tggl-client'
const cachedConfig = await loadConfig()
const client = new TgglLocalClient('YOUR_SERVER_API_KEY', {
initialConfig: cachedConfig
})
client.onConfigChange(async (config) => {
await saveConfig(config)
})
onConfigChange
is called only if the configuration changes when calling fetchConfig
manually or when polling, but not when calling setConfig
manually.
Alternatively, you can use the Proxy to handle caching and redundancy for you. This can even reduce costs as less calls will reach the API.
const client = new TgglLocalClient('YOUR_SERVER_API_KEY', {
baseUrl: 'http://your-proxy-domain.com'
})
// Will fetch config from the proxy
await client.fetchConfig()