Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions examples/example-cloudflare-kv-cache/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
POSTHOG_API_KEY=your_project_api_key
POSTHOG_PERSONAL_API_KEY=your_posthog_personal_api_key
POSTHOG_HOST=https://us.i.posthog.com
6 changes: 6 additions & 0 deletions examples/example-cloudflare-kv-cache/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
node_modules
.env
.DS_Store
.vscode
dist
.wrangler
99 changes: 99 additions & 0 deletions examples/example-cloudflare-kv-cache/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# PostHog Cloudflare KV Cache Example
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this contribution – what do you think about adding it to the website (e.g. a tutorial for flags or something) rather than burying it in the posthog-js examples directory? users can't grep this and don't really think to look here, so while it's useful for folks at e.g. posthog who are working on this stuff, I think it would be more powerful if it were on the website.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel this way generally; like I think any time we have how-to guides with something technical, it makes sense to write the docs on the website in addition (exclusively?) to wherever they're documented in code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point! I'll do a write up next week. 👍


This example demonstrates how to implement a distributed cache for PostHog feature flag definitions using Cloudflare KV storage. It showcases a split read/write pattern optimized for edge workers where low-latency flag evaluation is critical.

## Architecture

The example uses two specialized cache implementations:

- **`CloudflareKVFlagCacheReader`** - Read-only cache used in request handlers to evaluate flags locally without API calls
- **`CloudflareKVFlagCacheWriter`** - Write-only cache used in scheduled jobs to periodically refresh flag definitions

### How It Works

1. A Cloudflare Worker scheduled job (cron) runs every 5 minutes
2. The scheduled job fetches fresh flag definitions from PostHog and stores them in Cloudflare KV
3. Request handlers read flag definitions from KV and evaluate flags locally
4. No API calls are made during request handling, ensuring minimal latency

This pattern is ideal for high-traffic edge applications where:

- Flag evaluation must be extremely fast
- You want to minimize API calls to PostHog
- You can tolerate flag updates being slightly delayed (up to 5 minutes, or your own cron schedule)

## Prerequisites

- Node.js and pnpm installed
- A PostHog account with a project API key and personal API key

## Setup

### 1. Build Local Dependencies

From the root of the posthog-js repository:

```bash
pnpm i
pnpm package
```

### 2. Configure Environment Variables

Copy the example environment file and fill in your PostHog credentials:

```bash
cp .env.example .env
```

Edit `.env` and set:

```
POSTHOG_API_KEY=your_project_api_key
POSTHOG_PERSONAL_API_KEY=your_posthog_personal_api_key
POSTHOG_HOST=https://us.i.posthog.com
```

### 3. Build the Example

```bash
pnpm build
```

## Running Locally

### Start the Cloudflare Worker

```bash
pnpm dev
```

### Trigger the Scheduled Job

Manually trigger the cache update to populate flag definitions:

```bash
curl http://127.0.0.1:8788/cdn-cgi/handler/scheduled
```

Expected response:

```
ok
```

### Test Flag Evaluation

Request the worker endpoint to evaluate a flag locally:

```bash
curl http://127.0.0.1:8787
```

Expected response:

```json
{ "userId": "wvhqg4goolm", "feature": "beta-feature", "enabled": true }
```

The `userId` will be randomly generated for each request, and `enabled` will depend on your flag configuration in PostHog.
22 changes: 22 additions & 0 deletions examples/example-cloudflare-kv-cache/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"name": "example-cloudflare-kv-cache",
"version": "1.0.0",
"main": "dist/index.js",
"license": "MIT",
"scripts": {
"build": "pnpm exec tsc",
"dev": "wrangler dev",
"dev:local": "node dist/index.js",
"kv:create": "wrangler kv:namespace create POSTHOG_CACHE"
},
"devDependencies": {
"@cloudflare/workers-types": "^4.20251119.0",
"@tsconfig/recommended": "^1.0.13",
"@types/node": "^24.10.1",
"typescript": "^5.9.3",
"wrangler": "^4.49.0"
},
"dependencies": {
"posthog-node": "*"
}
}
Loading
Loading