Content Modules

Feature flags and settings with the Config module

Updated 90 views

Config is a key-value store for values that should not live in your codebase: feature flags, banner copy, discount rules, support email addresses.

Reading a value

TypeScript
const flag = await kookee.config.getByKey('feature-new-checkout');

if (flag.value === true) {
  // enabled
}

The response shape is minimal — a key and a value. Keys are lowercase letters, numbers and hyphens.

Values are any JSON

This is where Config earns its place. A whole UI configuration fits under one key:

TypeScript
const banner = await kookee.config.getByKey('homepage-banner');
// { text: 'Summer Sale!', enabled: true, color: '#ff6b00' }

Or a business rule your finance team owns:

TypeScript
const discount = await kookee.config.getByKey('pricing-discount');
// { percentage: 20, minOrderValue: 50 }

Grouping related settings into one object also means they change atomically. Three separate keys can be read midway through an edit, giving you a banner with new text and an old color.

Batch your reads

Fetching keys one at a time is the common mistake — every call counts against your rate limit and your monthly requests.

TypeScript
// One request
const configs = await kookee.config.list({
  keys: ['homepage-banner', 'feature-new-checkout', 'pricing-discount'],
});

// Every enabled key in the project
const all = await kookee.config.list();

Read config once per server render, or once at client boot, and pass values down. On the Free plan's 300 requests a minute per project and 100,000 a month across all your projects, per-component config reads will exhaust your budget quickly.

Always keep a safe default

An unreachable API should degrade to known-good behavior, not throw on your checkout page:

TypeScript
let showNewCheckout = false; // safe default in code

try {
  const flag = await kookee.config.getByKey('feature-new-checkout');
  showNewCheckout = flag.value === true;
} catch {
  // network hiccup — keep the default
}

Compare explicitly against the type you expect. flag.value === true is safer than a truthiness check, since a value edited to the string "false" is truthy.

Version history

Every change creates a snapshot, and any version from the last 30 days can be restored from the dashboard with one click.

That changes who can safely hold a key. The worst case for a mistyped discount is not an incident and a hotfix — it is a rollback performed by the person who made the mistake, seconds after noticing.

Write a description for every key when you create it. enable-v2 means nothing to whoever inherits it in six months, and Config is searchable by key and description.

Where Config stops

Config is deliberately simple: a key, a value, and history. It has no percentage rollouts, no per-user targeting, no A/B assignment. To show a feature to 5% of users in one country, use a dedicated experimentation platform.

What Config covers is the far more common case — a global value your team needs to change without a deploy. That is most flags, most of the time, and it lives behind the same API key as the rest of your content instead of adding a vendor.

Naming keys

Consistency beats cleverness, since keys are what you search. A prefix convention works well:

  • feature-* for flags — feature-new-checkout

  • ui-* for presentation — ui-homepage-banner

  • limit-* for business rules — limit-free-plan-projects

Prefixes also make list({ keys: [...] }) calls easy to assemble per surface.

Was this article helpful?