Skip to content

feat: Add feature flags with PostHog integration (ESM)#2160

Open
trillium wants to merge 7 commits into
hackforla:developmentfrom
trillium:fix/pr-2096-feature-flags-esm
Open

feat: Add feature flags with PostHog integration (ESM)#2160
trillium wants to merge 7 commits into
hackforla:developmentfrom
trillium:fix/pr-2096-feature-flags-esm

Conversation

@trillium
Copy link
Copy Markdown
Member

@trillium trillium commented May 19, 2026

Note: Conflict resolution, ESM conversion, and test coverage in this PR were generated with AI assistance (Claude Code) by @trillium. The feature flags implementation is @kkchu791's work from #2096.

Feature Flags with PostHog Integration (ESM)

Rebases and resolves merge conflicts from #2096 by @kkchu791 onto current development, with full ESM conversion and expanded test coverage.

Background

@kkchu791 built out a feature flags system using PostHog in #2096. That PR developed merge conflicts after the backend ESM migration (#2134) landed on development. This PR carries his work forward — all the feature flag logic is his design, we just resolved the conflicts, converted to ESM, and added tests.

What's included

Feature flags system (@kkchu791's work from #2096):

  • PostHog integration for backend feature flag evaluation
  • featureFlags.service.js — wraps PostHog getAllFlags(distinctId)
  • featureFlags.controller.js — validates custom header, resolves user identity, returns flags
  • featureFlags.router.jsGET /api/featureFlags with optional auth middleware
  • addCookieIfAvailable middleware — optional JWT parsing for personalized flags
  • Client-side FeatureFlagProvider context + useFeatureFlags hook
  • PostHog initialization in client entry point
  • posthog.identify() on auth for user-level flag targeting
  • Dockerfile bumped from node 18 → 22

Conflict resolution + ESM conversion:

  • All require()/module.exportsimport/export
  • .js extensions on relative imports
  • Test mocks converted from Jest to Vitest

Test coverage (new):

Area Tests Coverage
Controller 9 Header validation, distinctId routing, error handling, response body
Service 4 PostHog SDK wrapper, error propagation, edge cases
Middleware 4 JWT parsing, no-cookie fallback, non-blocking behavior
Router 3 Route wiring, middleware chain, integration
Client API 5 Fetch config, headers, credentials, response parsing
Client Context 5 Provider rendering, loading states, error resilience, context guard

All 109 backend tests and 15 client tests passing.

Credit

All feature flag implementation is @kkchu791's work from #2096. This PR resolves the merge conflicts from the ESM migration and adds test coverage.

Closes #2096

trillium and others added 7 commits May 18, 2026 18:58
Rebase of PR hackforla#2096 onto current development branch, with all code
converted from CommonJS to ESM to match the codebase's module system.

Backend changes:
- Add FeatureFlagsController with custom header validation
- Add FeatureFlagsService wrapping posthog-node client
- Add featureFlags router with addCookieIfAvailable middleware
- Add addCookieIfAvailable to auth middleware (optional JWT decode)
- Register featureFlags route in app.js
- Add posthog-node dependency
- Bump Dockerfile.api to node:22
- Add controller test using vitest (converted from jest)
- Add services/index.js barrel export

Client changes:
- Add FeatureFlagProvider context with useFeatureFlags hook
- Add featureFlagApiService for fetching flags
- Wrap App with FeatureFlagProvider
- Initialize posthog-js in index.jsx
- Add posthog.identify in authContext fetchAuth
- Add posthog-js dependency

Other:
- Add .env.test to .gitignore

Original PR: hackforla#2096
- Expand controller tests: header validation, distinctId fallback, response body
- Add service tests: PostHog SDK wrapper, error propagation
- Add middleware tests: addCookieIfAvailable JWT handling
- Add router integration tests: route wiring, middleware chain
- Add client tests: API service fetch params, context provider lifecycle

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Cut tests that verify JavaScript operators, Express internals, or
duplicate assertions already covered by other tests.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Previous lockfile had parse errors causing Docker build failures in CI.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Backend unit: replace --testPathIgnorePatterns (Jest) with test:unit
  script using vitest --exclude
- Backend integration: replace 'backend/routers/' path filter (wrong
  cwd inside Docker) with test:integration script using relative paths
- Client: remove USER node from client-development Dockerfile stage to
  fix EACCES when Vite writes temp config files in mounted volume

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
- Mount ./shared:/srv/shared in docker-compose so backend container
  can resolve '../../shared/authorizationUtils.js' imports
- Use vi.hoisted() to set CUSTOM_REQUEST_HEADER before controller
  module loads, fixing false 400 when env var is unset in CI

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Claude <noreply@anthropic.com>
Co-Authored-By: Happy <yesreply@happy.engineering>
Copy link
Copy Markdown
Member

@kkchu791 kkchu791 left a comment

Choose a reason for hiding this comment

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

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants