---
title: Cache Flagpole evaluations with Drift
description: Keep Flagpole as the release source of truth while caching short-lived evaluation responses inside a Node.js service.
url: https://pr-15-c0fdf0ef9f59.thally.app/guides/flagpole-with-drift
---

# Cache Flagpole evaluations with Drift

Keep Flagpole as the release source of truth while caching short-lived evaluation responses inside a Node.js service.

This pattern reduces repeated Flagpole requests on a hot application path.
Flagpole remains authoritative; Drift keeps each decision only for a short,
explicit TTL inside one process.

## Before you begin

- Run Flagpole and create `new-checkout` by following the
  [Flagpole quickstart](/flagpole/quickstart).
- Use Node.js 20 or later so the consumer has built-in `fetch`.
- From the consuming application's directory, clone and build the unpublished
  Drift package in a sibling directory, then install that local build:

  ```bash
  git clone https://github.com/kenny-io/driftkv.git ../driftkv
  npm --prefix ../driftkv install
  npm --prefix ../driftkv run build
  npm install ../driftkv
  ```

## Create the evaluation cache

```typescript
import { createStore } from "driftkv";

interface FlagEvaluation {
  key: string;
  enabled: boolean;
  rolloutPercentage?: number;
  environment?: string;
}

const evaluations = createStore<FlagEvaluation>({
  maxEntries: 5_000,
  defaultTtlMs: 5_000,
});

const flagpoleUrl = process.env.FLAGPOLE_URL ?? "http://localhost:3333";
const flagpoleToken = process.env.FLAGPOLE_API_TOKEN;

export async function evaluateFlag(
  flagKey: string,
  unit: string,
  environment = "production",
): Promise<FlagEvaluation> {
  const cacheKey = `${flagKey}:${environment}:${unit}`;
  const cached = evaluations.get(cacheKey);
  if (cached !== undefined) return cached;

  const url = new URL(`/v1/flags/${encodeURIComponent(flagKey)}/evaluate`, flagpoleUrl);
  url.searchParams.set("unit", unit);
  url.searchParams.set("environment", environment);

  const response = await fetch(url, {
    headers: flagpoleToken
      ? { authorization: `Bearer ${flagpoleToken}` }
      : undefined,
  });

  if (!response.ok) {
    throw new Error(`Flagpole evaluation failed with ${response.status}`);
  }

  const evaluation = (await response.json()) as FlagEvaluation;
  evaluations.set(cacheKey, evaluation);
  return evaluation;
}
```

The cache key includes the flag, environment, and stable rollout unit. Omitting
any of those inputs could return a decision made for a different cohort.

## Use the decision

```typescript
const checkout = await evaluateFlag(
  "new-checkout",
  request.accountId,
  "production",
);

if (checkout.enabled) {
  return renderNewCheckout();
}

return renderCurrentCheckout();
```

The first call reaches Flagpole. Repeated calls with the same inputs during the
next five seconds return the Drift entry. After expiry, the next read removes
the stale entry and fetches a current decision.

## Choose the failure policy explicitly

The example throws when Flagpole is unavailable and no live cached decision
exists. That is fail-closed at the call site because the application must
handle the error. If your product should fall back to the old experience,
catch the error there and return `enabled: false`. Do not silently extend an
expired evaluation forever; doing so can defeat a Flagpole kill switch.

Each application process owns its own Drift cache. A Flagpole change reaches
processes independently as their entries expire. Pick a TTL that matches the
maximum propagation delay your release policy can tolerate.