Skip to content

fix metrics queries#880

Merged
BilalG1 merged 5 commits intodevfrom
anon-user-queries-fix
Sep 10, 2025
Merged

fix metrics queries#880
BilalG1 merged 5 commits intodevfrom
anon-user-queries-fix

Conversation

@BilalG1
Copy link
Contributor

@BilalG1 BilalG1 commented Sep 6, 2025


Important

Fixes handling of isAnonymous field in metrics queries by using COALESCE to default null values to 'false' in route.tsx.

  • Behavior:
    • Fixes handling of isAnonymous field in metrics queries by using COALESCE to default null values to 'false'.
    • Affects loadUsersByCountry, loadDailyActiveUsers, and loadRecentlyActiveUsers functions in route.tsx.
  • Functions:
    • loadUsersByCountry: Updates query condition to use COALESCE for isAnonymous.
    • loadDailyActiveUsers: Updates query condition to use COALESCE for isAnonymous.
    • loadRecentlyActiveUsers: Updates query condition to use COALESCE for isAnonymous.

This description was created by Ellipsis for b3ad219. You can customize this summary. It will automatically update as commits are pushed.

Review by RecurseML

🔍 Review performed on 9318e2b..b3ad219

✨ No bugs found, your code is sparkling clean

✅ Files analyzed, no issues (1)

apps/backend/src/app/api/latest/internal/metrics/route.tsx

Need help? Join our Discord

Summary by CodeRabbit

  • Bug Fixes
    • Corrected handling of anonymous users in analytics: records missing an isAnonymous flag are now treated as non-anonymous when anonymous users are excluded.
    • Impacts country breakdown, daily active users, and recently active metrics; overall total users unchanged.
    • Expect slightly higher non-anonymous counts and more consistent reporting across these metrics.

@vercel
Copy link

vercel bot commented Sep 6, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
stack-backend Ready Ready Preview Comment Sep 9, 2025 10:52pm
stack-dashboard Ready Ready Preview Comment Sep 9, 2025 10:52pm
stack-demo Ready Ready Preview Comment Sep 9, 2025 10:52pm
stack-docs Ready Ready Preview Comment Sep 9, 2025 10:52pm

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 6, 2025

Walkthrough

Replaced three SQL conditions checking the isAnonymous flag with COALESCE-based checks so NULL/missing isAnonymous values are treated as non-anonymous. Changes are in loadUsersByCountry, loadDailyActiveUsers, and loadRecentlyActiveUsers within the metrics route; no exported/public signatures changed.

Changes

Cohort / File(s) Summary
Metrics anonymity filter normalization
apps/backend/src/app/api/latest/internal/metrics/route.tsx
Rewrote isAnonymous checks to use COALESCE("data"->>'isAnonymous', 'false') != 'true' in three queries: loadUsersByCountry, loadDailyActiveUsers (CASE), and loadRecentlyActiveUsers. NULL/missing isAnonymous now treated as not anonymous. No API signatures changed.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant C as Client
  participant M as Metrics Route
  participant DB as Database

  C->>M: GET /internal/metrics?include_anonymous={flag}
  alt include_anonymous = true
    M->>DB: Run queries without isAnonymous exclusion
    note right of DB: Return all rows
  else include_anonymous = false
    M->>DB: Run queries with filter:<br/>COALESCE(data->>'isAnonymous','false') != 'true'
    note right of DB: Exclude rows where isAnonymous = 'true',<br/>treat NULL as 'false'
  end
  DB-->>M: Aggregated results (country / daily / recent)
  M-->>C: JSON metrics
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Pre-merge checks (2 passed, 1 warning)

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title Check ✅ Passed The title “fix metrics queries” clearly and succinctly reflects the primary change of correcting the metrics query logic, specifically the handling of the isAnonymous field, without introducing unrelated details. It remains concise and focused on the main intent of the PR.
Description Check ✅ Passed The pull request description includes the required contributing guidelines comment from the repository template and provides a clear, structured summary of the change, its behavior, and the specific functions affected, meeting the repository’s description requirements.

Poem

I thump my paws at NULLs that hide,
With COALESCE carrots by my side.
If truth is true, I’ll let it show—
Missing bits? I’ll count them low.
Three hops through metrics, morning bright—🐇🥕

Tip

👮 Agentic pre-merge checks are now available in preview!

Pro plan users can now enable pre-merge checks in their settings to enforce checklists before merging PRs.

  • Built-in checks – Quickly apply ready-made checks to enforce title conventions, require pull request descriptions that follow templates, validate linked issues for compliance, and more.
  • Custom agentic checks – Define your own rules using CodeRabbit’s advanced agentic capabilities to enforce organization-specific policies and workflows. For example, you can instruct CodeRabbit’s agent to verify that API documentation is updated whenever API schema files are modified in a PR. Note: Upto 5 custom checks are currently allowed during the preview period. Pricing for this feature will be announced in a few weeks.

Example:

reviews:
  pre_merge_checks:
    custom_checks:
      - name: "Undocumented Breaking Changes"
        mode: "warning"
        instructions: |
          Pass/fail criteria: All breaking changes to public APIs, CLI flags, environment variables, configuration keys, database schemas, or HTTP/GraphQL endpoints must be documented in the "Breaking Change" section of the PR description and in CHANGELOG.md. Exclude purely internal or private changes (e.g., code not exported from package entry points or explicitly marked as internal).

Please share your feedback with us on this Discord post.


📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b3ad219 and 190e8d6.

📒 Files selected for processing (1)
  • apps/backend/src/app/api/latest/internal/metrics/route.tsx (3 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/backend/src/app/api/latest/internal/metrics/route.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: all-good
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: restart-dev-and-test
  • GitHub Check: setup-tests
  • GitHub Check: build (22.x)
  • GitHub Check: docker
  • GitHub Check: build (22.x)
  • GitHub Check: docker
  • GitHub Check: Security Check
✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch anon-user-queries-fix

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Greptile Summary

This PR fixes a critical data handling bug in the metrics API where NULL values in the isAnonymous field were causing incorrect user filtering. The changes update three SQL queries in /apps/backend/src/app/api/latest/internal/metrics/route.tsx to use COALESCE("data"->>'isAnonymous', 'false') != 'true' instead of the previous direct comparison NOT "data"->>'isAnonymous' = 'true'.

The root issue stems from PostgreSQL's NULL handling behavior - when isAnonymous is NULL, the original condition NOT "data"->>'isAnonymous' = 'true' would evaluate to NULL (not true), causing those records to be incorrectly excluded from non-anonymous user queries. The fix ensures that NULL values are treated as 'false' (non-anonymous), which aligns with the default behavior established in the database schema migration 20250320223454_anonymous_users where the isAnonymous column defaults to false.

The changes are applied consistently across three key analytics functions:

  • loadUsersByCountry: Filters users by country while respecting anonymous user preferences
  • loadDailyActiveUsers: Counts distinct active users per day
  • loadRecentlyActiveUsers: Retrieves recently active user data

This fix ensures accurate metrics reporting and prevents undercounting of users in the analytics dashboard, maintaining data integrity across the user tracking system.

Confidence score: 5/5

  • This PR is safe to merge with minimal risk as it fixes a clear data handling bug with a well-understood solution
  • Score reflects the straightforward nature of the fix using standard SQL NULL handling patterns and consistent application across affected queries
  • No files require special attention as the changes are isolated to a single metrics file with clear, targeted fixes

1 file reviewed, no comments

Edit Code Review Bot Settings | Greptile

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/backend/src/app/api/latest/internal/metrics/route.tsx (1)

41-45: Narrow types before Object.fromEntries to satisfy Record<string, number> and avoid nullable keys.

Filtering after map doesn’t narrow the tuple’s key type; this can trip strict TS. Filter first with a type guard.

Apply:

-  const rec = Object.fromEntries(
-    a.map(({ userCount, countryCode }) => [countryCode, Number(userCount)])
-      .filter(([countryCode, userCount]) => countryCode)
-  );
+  const rec = Object.fromEntries(
+    a
+      .filter(
+        (row): row is { countryCode: string; userCount: bigint } =>
+          row.countryCode !== null
+      )
+      .map(({ countryCode, userCount }) => [countryCode, Number(userCount)])
+  );

Optional: if counts can exceed 2^53-1, return stringified counts instead of Number to avoid precision loss.

🧹 Nitpick comments (3)
apps/backend/src/app/api/latest/internal/metrics/route.tsx (3)

30-31: COALESCE tweak is correct; consider casting to boolean to avoid string compares.

Avoid text comparison on JSON. Casting yields simpler, safer predicates.

Apply:

-AND (${includeAnonymous} OR COALESCE("data"->>'isAnonymous', 'false') != 'true')
+AND (${includeAnonymous} OR COALESCE(("data"->>'isAnonymous')::boolean, false) = false)

90-96: Simplify COUNT with FILTER and normalize userId to text.

FILTER is clearer and casting avoids JSON identity pitfalls (e.g., 1 vs "1").

Apply:

-        COUNT(DISTINCT CASE WHEN (${includeAnonymous} OR COALESCE("data"->>'isAnonymous', 'false') != 'true') THEN "data"->'userId' ELSE NULL END) AS "dau"
+        COUNT(DISTINCT ("data"->>'userId'))
+          FILTER (WHERE ${includeAnonymous} OR COALESCE(("data"->>'isAnonymous')::boolean, false) = false) AS "dau"

149-151: Mirror the boolean cast pattern here for consistency.

Keep all isAnonymous checks using boolean casting.

Apply:

-AND (${includeAnonymous} OR COALESCE("data"->>'isAnonymous', 'false') != 'true')
+AND (${includeAnonymous} OR COALESCE(("data"->>'isAnonymous')::boolean, false) = false)
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 9318e2b and b3ad219.

📒 Files selected for processing (1)
  • apps/backend/src/app/api/latest/internal/metrics/route.tsx (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
apps/backend/src/app/api/latest/**

📄 CodeRabbit inference engine (AGENTS.md)

apps/backend/src/app/api/latest/**: Organize backend API routes by resource under /api/latest (e.g., auth at /api/latest/auth/, users at /api/latest/users/, teams at /api/latest/teams/, oauth providers at /api/latest/oauth-providers/)
Use the custom route handler system in the backend to ensure consistent API responses

Files:

  • apps/backend/src/app/api/latest/internal/metrics/route.tsx
**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (AGENTS.md)

Prefer ES6 Map over Record when representing key–value collections

Files:

  • apps/backend/src/app/api/latest/internal/metrics/route.tsx
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: lint_and_build (latest)
  • GitHub Check: build (22.x)
  • GitHub Check: all-good
  • GitHub Check: restart-dev-and-test
  • GitHub Check: docker
  • GitHub Check: docker
  • GitHub Check: build (22.x)
  • GitHub Check: setup-tests
  • GitHub Check: Security Check
🔇 Additional comments (2)
apps/backend/src/app/api/latest/internal/metrics/route.tsx (2)

182-209: Request schema alignment check for API contract.

users_by_country returns an object map. If any client expects a Map, confirm serialization. Given JSON, object is correct; just verifying downstream expectations.


68-69: No update required for isAnonymous filters.
The ProjectUser.isAnonymous field is defined as non-nullable Boolean with a default of false in schema.prisma and the SQL migration, so it never contains NULL and the existing pu."isAnonymous" = false (and { isAnonymous: false }) filters are correct.

@BilalG1 BilalG1 merged commit 3b6f9ee into dev Sep 10, 2025
16 of 19 checks passed
@BilalG1 BilalG1 deleted the anon-user-queries-fix branch September 10, 2025 02:22
sicarius97 pushed a commit to sicarius97/stack-auth that referenced this pull request Sep 10, 2025
<!--

Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md

-->

<!-- ELLIPSIS_HIDDEN -->

----

> [!IMPORTANT]
> Fixes handling of `isAnonymous` field in metrics queries by using
`COALESCE` to default null values to 'false' in `route.tsx`.
> 
>   - **Behavior**:
> - Fixes handling of `isAnonymous` field in metrics queries by using
`COALESCE` to default null values to 'false'.
> - Affects `loadUsersByCountry`, `loadDailyActiveUsers`, and
`loadRecentlyActiveUsers` functions in `route.tsx`.
>   - **Functions**:
> - `loadUsersByCountry`: Updates query condition to use `COALESCE` for
`isAnonymous`.
> - `loadDailyActiveUsers`: Updates query condition to use `COALESCE`
for `isAnonymous`.
> - `loadRecentlyActiveUsers`: Updates query condition to use `COALESCE`
for `isAnonymous`.
> 
> <sup>This description was created by </sup>[<img alt="Ellipsis"
src="https://img.shields.io/badge/Ellipsis-blue?color=175173">](https://www.ellipsis.dev?ref=stack-auth%2Fstack-auth&utm_source=github&utm_medium=referral)<sup>
for b3ad219. You can
[customize](https://app.ellipsis.dev/stack-auth/settings/summaries) this
summary. It will automatically update as commits are pushed.</sup>

<!-- ELLIPSIS_HIDDEN -->

<!-- RECURSEML_SUMMARY:START -->
## Review by RecurseML

_🔍 Review performed on
[9318e2b..b3ad219](stack-auth/stack-auth@9318e2b...b3ad2194304928c64612045e00dea9f3e518c3f0)_

✨ No bugs found, your code is sparkling clean

<details>
<summary>✅ Files analyzed, no issues (1)</summary>

  • `apps/backend/src/app/api/latest/internal/metrics/route.tsx`
</details>

[![Need help? Join our
Discord](https://img.shields.io/badge/Need%20help%3F%20Join%20our%20Discord-5865F2?style=plastic&logo=discord&logoColor=white)](https://discord.gg/n3SsVDAW6U)
<!-- RECURSEML_SUMMARY:END -->

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **Bug Fixes**
* Corrected handling of anonymous users in analytics: records missing an
isAnonymous flag are now treated as non-anonymous when anonymous users
are excluded.
* Impacts country breakdown, daily active users, and recently active
metrics; overall total users unchanged.
* Expect slightly higher non-anonymous counts and more consistent
reporting across these metrics.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

Co-authored-by: Konsti Wohlwend <n2d4xc@gmail.com>
@coderabbitai coderabbitai bot mentioned this pull request Oct 20, 2025
@coderabbitai coderabbitai bot mentioned this pull request Nov 6, 2025
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