Skip to content

refactor: ratelimit move rsc to trpc#3380

Merged
chronark merged 11 commits intomainfrom
ratelimit-move-rsc-to-trpc
Jun 19, 2025
Merged

refactor: ratelimit move rsc to trpc#3380
chronark merged 11 commits intomainfrom
ratelimit-move-rsc-to-trpc

Conversation

@ogzhanolguncu
Copy link
Contributor

@ogzhanolguncu ogzhanolguncu commented Jun 17, 2025

What does this PR do?

Move ratelimit RSC to tRPC and add some prefetches to make things faster.

Type of change

  • Bug fix (non-breaking change which fixes an issue)
  • Chore (refactoring code, technical debt, workflow improvements)
  • Enhancement (small improvements)
  • New feature (non-breaking change which adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • This change requires a documentation update

How should this be tested?

  • It should work as before.

Checklist

Required

  • Filled out the "How to test" section in this PR
  • Read Contributing Guide
  • Self-reviewed my own code
  • Commented on my code in hard-to-understand areas
  • Ran pnpm build
  • Ran pnpm fmt
  • Checked for warnings, there are none
  • Removed all console.logs
  • Merged the latest changes from main onto my branch with git pull origin main
  • My changes don't cause any responsiveness issues

Appreciated

  • If a UI change was made: Added a screen recording or screenshots to this PR
  • Updated the Unkey Docs if changes were necessary

Summary by CodeRabbit

  • New Features

    • Added "Last Used" tracking for rate limit identifiers with relative timestamps and tooltips in the overrides table.
    • Introduced skeleton loaders for namespace cards, metric stats, and settings pages to improve loading experience.
  • Refactor

    • Moved data fetching for namespaces, overrides, and settings to client-side components via TRPC queries, simplifying props and enhancing responsiveness.
    • Updated navigation components to fetch their own data with loading skeletons and dynamic imports.
    • Converted major pages from server-side to client-side components, removing async data fetching and simplifying data flow.
    • Added prefetching on namespace cards to improve navigation performance.
  • Bug Fixes

    • Enhanced badge styling for status indicators to improve visual consistency.
  • Chores

    • Removed deprecated server-side data fetching utilities and related props to streamline the codebase.
    • Added new TRPC query handlers to support client-side data fetching for rate limit details and last used times.

@changeset-bot
Copy link

changeset-bot bot commented Jun 17, 2025

⚠️ No Changeset found

Latest commit: 0d9291a

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 17, 2025

📝 Walkthrough

Walkthrough

This update refactors the rate limits dashboard to shift all data fetching from server-side to client-side using TRPC queries. Several page and component props are simplified to use only IDs, with components now responsible for their own data loading and skeleton states. New TRPC procedures and skeleton components are introduced, and obsolete server-side helpers are removed.

Changes

File(s) Change Summary
.../ratelimits/[namespaceId]/_overview/components/table/utils/get-row-class.ts Updated badge CSS classes for success status.
.../ratelimits/[namespaceId]/logs/page.tsx
.../ratelimits/[namespaceId]/page.tsx
.../ratelimits/[namespaceId]/settings/page.tsx
.../ratelimits/[namespaceId]/overrides/page.tsx
.../ratelimits/page.tsx
Converted pages from async server components to synchronous client components; removed server-side data fetching and related props; updated to use only namespaceId as prop.
.../ratelimits/[namespaceId]/namespace-navbar.tsx Refactored to fetch data via TRPC, handle loading state, and dynamically import IdentifierDialog; simplified props.
.../ratelimits/[namespaceId]/namespace.actions.ts Deleted server-side data fetching helpers (getWorkspaceDetails, getWorkspaceDetailsWithOverrides).
.../ratelimits/[namespaceId]/overrides/last-used-cell.tsx Added new LastUsedCell component to display last usage timestamp via TRPC.
.../ratelimits/[namespaceId]/overrides/overrides-table.tsx Refactored to fetch overrides data via TRPC; removed props for ratelimits and lastUsedTimes; uses LastUsedCell for "Last used" column.
.../ratelimits/[namespaceId]/settings/components/settings-client.tsx Refactored to fetch namespace data via TRPC; simplified to use namespaceId prop; added skeleton loading state.
.../ratelimits/[namespaceId]/settings/components/skeleton.tsx Added SettingsClientSkeleton component for loading state.
.../ratelimits/_components/namespace-card.tsx Added TRPC prefetch on mouse hover for namespace details.
.../ratelimits/_components/ratelimit-client.tsx Refactored to fetch namespaces via TRPC infinite query; removed prop-based data passing; added skeleton loading state.
.../ratelimits/_components/skeletons.tsx Added skeleton components: ChartSkeleton, MetricStatsSkeleton, NamespaceCardSkeleton.
components/stats-card/index.tsx Enabled link prefetching for stats card.
lib/trpc/routers/index.ts Registered new TRPC queries for last used and namespace details under ratelimit namespace router.
lib/trpc/routers/ratelimit/query-last-used-times.ts Added TRPC query procedure to fetch last usage timestamp for a rate limit identifier.
lib/trpc/routers/ratelimit/query-namespace-details/index.ts Added TRPC query procedure for detailed namespace and workspace info; includes types and helpers.
.../ratelimits/[namespaceId]/_components/namespace-delete-dialog.tsx Replaced cache invalidation via revalidateTag with TRPC query cache invalidation after namespace deletion.

Sequence Diagram(s)

sequenceDiagram
    participant Page
    participant Navbar
    participant Table
    participant TRPC
    participant DB

    Page->>Navbar: Render with namespaceId
    Navbar->>TRPC: queryDetails(namespaceId, includeOverrides?)
    TRPC->>DB: Fetch namespace, ratelimitNamespaces (and overrides)
    DB-->>TRPC: Data
    TRPC-->>Navbar: Data
    Navbar-->>Page: Rendered Navbar

    Page->>Table: Render with namespaceId
    Table->>TRPC: queryDetails(namespaceId, includeOverrides: true)
    TRPC->>DB: Fetch overrides
    DB-->>TRPC: Overrides data
    TRPC-->>Table: Overrides data
    loop For each override
        Table->>LastUsedCell: Render with namespaceId, identifier
        LastUsedCell->>TRPC: queryRatelimitLastUsed(namespaceId, identifier)
        TRPC->>DB: Fetch last used timestamp
        DB-->>TRPC: Timestamp
        TRPC-->>LastUsedCell: Timestamp
    end
Loading

Possibly related PRs

Suggested labels

Improvement, Needs Approval

Suggested reviewers

  • perkinsjr
  • mcstepp
  • chronark

📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0f59344 and 0d9291a.

📒 Files selected for processing (4)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/_components/namespace-delete-dialog.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/overrides/last-used-cell.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/settings/components/settings-client.tsx (4 hunks)
  • apps/dashboard/app/(app)/ratelimits/_components/ratelimit-client.tsx (2 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (3)
  • GitHub Check: Test Packages / Test ./packages/cache
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (15)
apps/dashboard/app/(app)/ratelimits/[namespaceId]/overrides/last-used-cell.tsx (6)

1-8: LGTM: Clean imports and dependencies

The imports are well-organized and all necessary dependencies are included for the component functionality.


9-12: LGTM: Clear type definition

The props interface is simple and correctly typed for the component's requirements.


14-21: LGTM: Proper data fetching and state management

The tRPC query setup and tooltip state management are implemented correctly.


31-36: LGTM: Proper tooltip event handling

The mouse event handlers for tooltip visibility are implemented correctly.


42-61: LGTM: Comprehensive state handling with correct timestamp usage

The conditional rendering logic properly handles all states (loading, error, success, no data) and correctly uses data.lastUsed instead of a hard-coded value, addressing the previous review concern.


50-58: LGTM: TimestampInfo integration is well-implemented

The TimestampInfo component is properly configured with correct props including the tooltip functionality and data binding.

apps/dashboard/app/(app)/ratelimits/_components/ratelimit-client.tsx (3)

23-33: LGTM: Clean tRPC infinite query setup

The infinite query configuration is well-structured with proper pagination support via getNextPageParam. The destructuring includes both loading and error states for comprehensive handling.


35-40: LGTM: Efficient data flattening with memoization

The useMemo hook properly flattens paginated data and handles the case when pages don't exist yet, preventing unnecessary recalculations.


53-67: Good error handling implementation

The error state handling has been implemented as suggested in previous reviews, showing a proper error fallback with the error message. The loading state shows appropriate skeleton placeholders.

apps/dashboard/app/(app)/ratelimits/[namespaceId]/_components/namespace-delete-dialog.tsx (2)

47-47: LGTM: Proper tRPC utils initialization

The trpc.useUtils() hook is correctly used to access tRPC utilities for cache management.


55-55: LGTM: Consistent cache invalidation with tRPC

The cache invalidation has been properly updated to use tRPC utilities instead of Next.js revalidateTag. This aligns with the broader refactoring to move from server-side to client-side data management.

apps/dashboard/app/(app)/ratelimits/[namespaceId]/settings/components/settings-client.tsx (4)

20-23: LGTM: Clean tRPC query implementation

The query setup is well-structured, fetching namespace details with the appropriate parameters. The destructuring includes loading state for proper UI handling.


27-31: Good: State update moved to useEffect

The state update logic has been properly moved from the render body to a useEffect hook, addressing the previous review concern about state updates during rendering. The condition ensures the name is only set when it's initially empty, preventing overwrites.


36-37: LGTM: Comprehensive cache invalidation

Both the general namespace query and the detailed query are invalidated after successful updates, ensuring data consistency across the application.


91-97: LGTM: Proper form handling with controlled submission

The form now uses proper onSubmit event handling with preventDefault(), providing better form semantics and user experience compared to the previous click-based approach.

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@github-actions
Copy link
Contributor

github-actions bot commented Jun 17, 2025

Thank you for following the naming conventions for pull request titles! 🙏

@vercel
Copy link

vercel bot commented Jun 17, 2025

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
dashboard ✅ Ready (Inspect) Visit Preview 💬 Add feedback Jun 19, 2025 11:01am
1 Skipped Deployment
Name Status Preview Comments Updated (UTC)
engineering ⬜️ Ignored (Inspect) Visit Preview Jun 19, 2025 11:01am

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: 25

🔭 Outside diff range comments (1)
apps/dashboard/app/(app)/ratelimits/[namespaceId]/overrides/overrides-table.tsx (1)

38-48: Minor perf: getRowClassName recreated every render

Since the return value is static, memoise once outside the component to avoid a new fn on every render (micro-optimisation, but free).

-const getRowClassName = () => {
+const getRowClassName = (() => {
   const style = STATUS_STYLES.default;
   return cn(
     style.base,
     style.hover,
     "group rounded-md",
     "focus:outline-none focus:ring-1 focus:ring-opacity-40",
     style.focusRing,
   );
-};
+})();
📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 503aeeb and b8b0ab0.

📒 Files selected for processing (19)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/table/utils/get-row-class.ts (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/page.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/namespace-navbar.tsx (2 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/namespace.actions.ts (0 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/overrides/last-used-cell.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/overrides/overrides-table.tsx (4 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/overrides/page.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/page.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/settings/components/settings-client.tsx (5 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/settings/components/skeleton.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/settings/page.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/_components/namespace-card.tsx (2 hunks)
  • apps/dashboard/app/(app)/ratelimits/_components/ratelimit-client.tsx (2 hunks)
  • apps/dashboard/app/(app)/ratelimits/_components/skeletons.tsx (1 hunks)
  • apps/dashboard/app/(app)/ratelimits/page.tsx (1 hunks)
  • apps/dashboard/components/stats-card/index.tsx (1 hunks)
  • apps/dashboard/lib/trpc/routers/index.ts (2 hunks)
  • apps/dashboard/lib/trpc/routers/ratelimit/query-last-used-times.ts (1 hunks)
  • apps/dashboard/lib/trpc/routers/ratelimit/query-namespace-details/index.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • apps/dashboard/app/(app)/ratelimits/[namespaceId]/namespace.actions.ts
⏰ Context from checks skipped due to timeout of 90000ms (9)
  • GitHub Check: Test Packages / Test ./packages/cache
  • GitHub Check: Test Packages / Test ./packages/api
  • GitHub Check: Test Packages / Test ./packages/nextjs
  • GitHub Check: Test Packages / Test ./packages/hono
  • GitHub Check: Test Packages / Test ./internal/resend
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: Test Packages / Test ./internal/encryption
  • GitHub Check: autofix
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (6)
apps/dashboard/app/(app)/ratelimits/_components/skeletons.tsx (1)

25-27: ```shell
#!/bin/bash
set -e

file="apps/dashboard/app/(app)/ratelimits/_components/skeletons.tsx"
echo "Inspecting file: $file"
echo

echo "=== Imports ==="
rg --line-number "^import.*Clock" "$file" || true
rg --line-number "^import.*ProgressBar" "$file" || true
echo

echo "=== Lines 1-80 ==="
sed -n '1,80p' "$file"


</details>
<details>
<summary>apps/dashboard/app/(app)/ratelimits/[namespaceId]/_overview/components/table/utils/get-row-class.ts (1)</summary>

`22-24`: **Looks good – style tweak only**

Border additions are self-contained and don’t affect logic. ✅

</details>
<details>
<summary>apps/dashboard/lib/trpc/routers/index.ts (1)</summary>

`70-75`: **Router wiring checks out**

New procedures are correctly imported and exposed at  
`ratelimit.namespace.queryDetails` and `ratelimit.namespace.queryRatelimitLastUsed`.

No conflicts detected with existing paths.  
Just ensure corresponding client hooks (`utils.ratelimit.namespace.queryDetails`) are regenerated after the change.




Also applies to: 210-213

</details>
<details>
<summary>apps/dashboard/app/(app)/ratelimits/[namespaceId]/logs/page.tsx (1)</summary>

`6-17`: **Same destructuring nitpick**

Inline–destructuring keeps the signature short and consistent with the other pages.

```diff
-export default function RatelimitLogsPage({
-  params: { namespaceId },
-}: {
-  params: { namespaceId: string };
-}) {
+export default function RatelimitLogsPage({
+  params: { namespaceId },
+}: {
+  params: { namespaceId: string };
+}) {

Likely an incorrect or invalid review comment.

apps/dashboard/lib/trpc/routers/ratelimit/query-last-used-times.ts (1)

11-15: Potential Number overflow

ClickHouse stores timestamps in milliseconds; current values already exceed 2^31. Using z.number() risks precision loss past Number.MAX_SAFE_INTEGER. Return an ISO string or bigint instead.

-const getLastUsedOutput = z.object({
-  identifier: z.string(),
-  lastUsed: z.number().nullable(),
+const getLastUsedOutput = z.object({
+  identifier: z.string(),
+  lastUsed: z.string().nullable(), // ISO date string
 });

Likely an incorrect or invalid review comment.

apps/dashboard/app/(app)/ratelimits/[namespaceId]/overrides/page.tsx (1)

6-21: No functional concerns – neat reduction in props

The component is now a lightweight client wrapper that simply forwards namespaceId, which fits the new TRPC-driven data-loading approach. Nothing blocking here.

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: 1

♻️ Duplicate comments (3)
apps/dashboard/lib/trpc/routers/ratelimit/query-namespace-details/index.ts (3)

8-8: The includeOverrides flag naming issue persists.

The includeOverrides flag still controls both override data and workspace functionality, which creates confusion for API consumers about what data will be included in the response.


78-102: Unbounded query still risks performance issues.

The database query fetches all ratelimitNamespaces without limits, which could cause memory and performance problems for workspaces with many namespaces or overrides.


111-111: Linear search inefficiency remains unaddressed.

The code still uses workspace.ratelimitNamespaces.find() which performs a linear search in memory instead of leveraging database indexing.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8b0ab0 and 0f59344.

📒 Files selected for processing (2)
  • apps/dashboard/app/(app)/ratelimits/page.tsx (1 hunks)
  • apps/dashboard/lib/trpc/routers/ratelimit/query-namespace-details/index.ts (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (6)
  • GitHub Check: Test Packages / Test ./packages/api
  • GitHub Check: Test Packages / Test ./packages/nextjs
  • GitHub Check: Test Packages / Test ./packages/rbac
  • GitHub Check: Test Packages / Test ./internal/clickhouse
  • GitHub Check: autofix
  • GitHub Check: Analyze (javascript-typescript)
🔇 Additional comments (4)
apps/dashboard/lib/trpc/routers/ratelimit/query-namespace-details/index.ts (1)

35-41: TRPC procedure structure looks solid.

The procedure properly uses authentication middleware, workspace context, and rate limiting. The input/output validation with Zod schemas provides good type safety.

apps/dashboard/app/(app)/ratelimits/page.tsx (3)

1-1: Successful conversion to client component.

The "use client" directive properly marks this as a client component, aligning with the PR's goal of moving data fetching from RSC to TRPC.


6-13: Clean and focused component structure.

The simplified component structure is well-organized, with clear separation of concerns between navigation and the main ratelimit functionality.


10-10: Verify RatelimitClient handles data fetching internally.

Since this component no longer passes any props to RatelimitClient, ensure that the client component properly handles its own data fetching and loading states via TRPC queries.

#!/bin/bash
# Description: Verify RatelimitClient component handles its own data fetching
# Expected: Find TRPC hooks or queries within RatelimitClient component

rg -A 5 -B 5 "trpc\.|useQuery|useMutation" apps/dashboard/app/\(app\)/ratelimits/_components/ratelimit-client.tsx

@chronark chronark added this pull request to the merge queue Jun 19, 2025
Merged via the queue into main with commit 4ca795b Jun 19, 2025
30 checks passed
@chronark chronark deleted the ratelimit-move-rsc-to-trpc branch June 19, 2025 14:09
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