Files
Richard SolomouandGitHub bf5ce2be1a feat(convex): move credentials and refresh cron onto the component (#3662)
* feat(convex)!: move credentials to component env vars

Use Convex 1.39 typed component env vars so the project token, host, and
personal API key live on the component (declared in convex.config.ts)
instead of being plumbed through every call site. The refresh cron now
lives inside the component too, gated on POSTHOG_PERSONAL_API_KEY so idle
dev deployments don't pay the per-tick cost. Renames POSTHOG_API_KEY to
POSTHOG_TOKEN.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* refactor(convex): remove manual refreshFlagDefinitions path

The cron handles refresh; exposing a manual method on the client was a
footgun and didn't match how the other PostHog server SDKs work. Drops
the client method, makes the component action internal (cron uses
internal.lib.refreshFlagDefinitions), and removes the demo's "Refresh"
button and its example.refreshFlags wrapper.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* fix(convex): warn on missing token and clarify cron activation docs

Address greptile review on #3662:

- readConfig now logs when POSTHOG_TOKEN is empty rather than dropping
  events silently. The typed env-var on the component should prevent
  this at deploy time, but stale isolates or post-deploy unsetting can
  still produce empty values and the warn gives operators a trail.
- README clarifies the cron gate fires at module-load (deploy) time:
  npx convex dev redeploys automatically when the env var is set,
  production deployments need a manual redeploy.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* feat(convex): throw on misconfigured local eval, allow tuning cron cadence

Two follow-ups on the v2 PR that close gaps Richard flagged in the docs
PR review:

- Local-eval methods now throw a clear error when POSTHOG_PERSONAL_API_KEY
  isn't set, pointing callers at the remote evaluateFlag methods rather
  than silently returning undefined. They still degrade gracefully
  (return undefined) during the brief warm-up window when PAK is set
  but the cron hasn't populated the cache yet.
- New POSTHOG_FLAGS_POLLING_INTERVAL_SECONDS env var (default 60) lets
  operators raise the cron cadence on free-tier dev deployments. Convex
  component env vars are string-typed on the wire, so we parse and
  validate in crons.ts at module-load with a warn-and-fallback on bad
  input.

The status surface comes from getFlagDefinitions, which now returns
{ localEvalConfigured, data, fetchedAt, etag } instead of a nullable row.
The client distinguishes "not configured" (throw) from "configured but
not warmed up" (undefined) based on that flag.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* feat(convex): bring back reloadFeatureFlags and fill test coverage gaps

Restores manual refresh under a posthog-node-compatible name: the
component's refreshFlagDefinitions action is public again, and the
client exposes posthog.reloadFeatureFlags(ctx) for on-demand refresh
between cron ticks (e.g. after creating a flag in development).

Test coverage:

- New crons.test.ts unit-tests readPollingIntervalSeconds across
  unset/empty/whitespace/valid/non-numeric/zero/negative/fractional.
- New describe block in example.test.ts exercises getFlagDefinitions
  end-to-end with PAK set and unset, guarding the localEvalConfigured
  surface the client relies on to choose throw-vs-undefined.
- Client tests cover reloadFeatureFlags shape + delegation.

Docs:

- README documents reloadFeatureFlags alongside the cron.
- New "Differences from posthog-node" section calls out the ctx-first
  shape, args-object methods, no Immediate/flush/shutdown variants,
  no auto-fallback for local eval, and the throw-on-missing-PAK choice.

143 passing tests (up from 131).

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* fix(convex): trim PAK env var before deciding whether to register the cron

graphite review on #3662: `if (process.env.POSTHOG_PERSONAL_API_KEY)`
treats `" "` as truthy and registers the cron, but the action's
readConfig() trims the same value and rejects it, so every tick is a
no-op that still burns a function call. Trim at the gate to match
how the rest of the component interprets the env var.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* docs(convex): tighten 'Differences from posthog-node' section

Two bullets in the differences list were actually describing
similarities:

- 'Method args are objects, not positional' is only true for the
  flag methods and captureException. capture/identify/alias/
  groupIdentify already use args objects in posthog-node.
- 'reloadFeatureFlags matches posthog-node's naming' is literally
  a similarity — doesn't belong in a differences list.

Rewrote the args-object bullet to scope it correctly and dropped
the reloadFeatureFlags bullet (it's documented earlier in the
Feature Flags section already).

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* refactor(convex): use typed env import from _generated/server, not process.env

Per https://docs.convex.dev/components/authoring#environment-variables,
the typed `env` import from `_generated/server` is the recommended way
to read declared component env vars — `process.env` works but loses
the type safety the env declarations exist to provide.

Adds a typed `env` export to `_generated/server.ts` (we hand-edit
generated files anyway because there's no live deployment for
`npx convex codegen` to run against), switches `readConfig()` in lib.ts
and the cron registration + interval parsing in crons.ts to use it.

The test-only POSTHOG_FLAGS_RETRY_DELAY_MS_OVERRIDE escape hatch in
lib.ts stays on process.env — it's not a declared component env var.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* chore(convex): automate _generated/ regeneration via local-only convex dev

The component's _generated/ files now come from `npx convex codegen`
against a Convex local-only dev deployment, instead of being hand-edited.

build:codegen first bootstraps a local-only deployment (no Convex
cloud account or login required) into .env.local, then runs codegen
against it. The bootstrap's push step intentionally fails because the
example-convex app reads env vars at module-load that aren't set, but
.env.local is written first so the codegen on the next step succeeds.

.env.local was already covered by *.local in .gitignore. Local backend
state lives in ~/.convex/, outside the repo.

CONTRIBUTING.md documents the workflow. The _generated/ files in this
commit are what the official codegen produces; they're almost identical
to what was hand-written, plus a few cosmetic differences and the
version.ts import which the prebuild script writes.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* docs(convex): address Rafa's README review feedback

- Add per-line comments to the convex.config.ts code block explaining
  what each declared env var does and which are required vs optional.
- Migration guide step 1 said "Bump your Convex peer dep" — the
  peer dep is ours, not theirs; users have convex as a regular dep
  in their app. Reword.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* docs(convex): explain POSTHOG_API_KEY → POSTHOG_TOKEN rename rationale

Per Rafa's read of the PR: the rename's purpose (differentiating the
project token from the personal API key) wasn't in the changelog or
migration guide. Add it to both so future readers understand why
the rename happened.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* docs(convex): call out POSTHOG_TOKEN now required at deploy time

Ian flagged on the PR that declaring POSTHOG_TOKEN as v.string()
(required) means deploys without it set will now fail at deploy
time. In v1 the env var was a plain process.env read so deployments
went through and event sends silently no-op'd at runtime.

This is intentional for v2 (a major bump) since the component
literally can't function without a token — fail-fast at deploy
is better than silent runtime no-ops. Just need to be explicit
about it for v1 → v2 upgraders.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c

* refactor(convex): rename POSTHOG_TOKEN to POSTHOG_PROJECT_TOKEN

POSTHOG_TOKEN was unambiguous on its own but landed next to
POSTHOG_PERSONAL_API_KEY in the same env-var block, where "token"
vs "API key" reads more like a casual style difference than two
genuinely different credentials. POSTHOG_PROJECT_TOKEN makes the
distinction explicit (project credential vs personal credential)
on every line a user looks at.

Touches:
- src/component/convex.config.ts — env var declaration
- src/component/lib.ts — readConfig, getClient, action handlers,
  refresh URL builder; local `token` variables also renamed to
  `projectToken` for consistency. The wire-protocol `?token=`
  query param and `<token>` log placeholder stay (not ours)
- src/component/_generated/server.ts — regenerated via codegen
- README quick start, migration guide, code-block annotations
- example-convex convex.config.ts, posthog.ts, example.test.ts,
  README, and the two OTel exporters (so the example uses one
  env var for both convex events and OTel traces)
- changeset

143 tests passing, lint clean, example typechecks.

Generated-By: PostHog Code
Task-Id: 9f07d5c6-34a2-4427-a0ac-23af2882a87c
2026-05-27 21:12:13 +03:00

1.5 KiB
Raw Permalink Blame History

Example app — @posthog/convex

Exercises every method the component exposes — analytics, local feature flag eval, remote feature flag eval, and AI generation tracing — against a real PostHog project. Useful for verifying changes to the component end-to-end and as a runnable reference for setup.

Running it

From the repository root:

pnpm i
pnpm package        # builds the tarball that this app installs
pnpm dev --filter example-convex

In a second terminal:

cd examples/example-convex
npx convex dev
npx convex env set POSTHOG_PROJECT_TOKEN phc_…             # project token
npx convex env set POSTHOG_PERSONAL_API_KEY phs_…  # optional, enables local eval
npx convex env set POSTHOG_HOST https://us.i.posthog.com   # optional, US default

What you'll see

  • Sections 01–03 capture analytics events (verify them in your PostHog activity feed).
  • Section 04 has two rows of buttons — local-eval methods (query context, reactive) and remote-eval methods (action context, per-call /flags request).
  • Section 05 captures $ai_generation events through @posthog/ai, defaulting to OpenTelemetry with manual capture as the alternative. See LLM analytics for Convex.
  • The right column shows the local evaluation cache state plus a live, reactive view of flag values for the current Distinct ID — change a flag in PostHog and the row flashes when the cron picks it up.