Skip to content

feat: connect backend via trpc, homepage group search#123

Open
lorenzocorallo wants to merge 3 commits into
mainfrom
backend-groups
Open

feat: connect backend via trpc, homepage group search#123
lorenzocorallo wants to merge 3 commits into
mainfrom
backend-groups

Conversation

@lorenzocorallo
Copy link
Copy Markdown
Member

No description provided.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 29, 2026

Review Change Stack

Warning

Review limit reached

@lorenzocorallo, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 55 minutes and 30 seconds. Learn how PR review limits work.

Your organization has run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans include higher PR review limits than trial, open-source, and free plans. In all cases, reviews become available again over time. During sustained high-volume PR review activity, CodeRabbit may temporarily slow when the next review becomes available.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 1575c0ea-2c12-4939-8cbe-da02a203590a

📥 Commits

Reviewing files that changed from the base of the PR and between 87109a7 and 27b76b2.

📒 Files selected for processing (2)
  • src/components/home/group-search.tsx
  • src/components/home/hero.tsx

Walkthrough

This PR integrates tRPC for backend group search functionality. It adds environment variables, type definitions, a typed tRPC client, a server query function, a debounced group search component with dropdown results, a reusable spinner component, and wires the search into the Hero component.

Changes

tRPC Group Search Feature

Layer / File(s) Summary
Dependencies and environment setup
package.json, .env.example, src/env.js
Adds tRPC, SuperJSON, and backend packages; configures BACKEND_URL environment variable with validation and runtime exposure.
API type definitions
src/types.ts
Exports ApiOutput, ApiInput, and ApiError type aliases inferred from the tRPC router.
tRPC client initialization
src/lib/backend.ts
Creates a typed tRPC client with HTTP batch link, SuperJSON transformer, and backend URL construction.
Server-side group search query
src/queries/groups.ts
Implements searchGroups(query, limit) function that calls the tRPC backend search endpoint.
Search UI components
src/components/spinner.tsx, src/components/home/group-search.tsx
Adds accessible spinner component and debounced group search input with results dropdown and link rendering.
Hero section integration
src/components/home/hero.tsx
Replaces inline search input with GroupSearch component.
🚥 Pre-merge checks | ✅ 3 | ❌ 1

❌ 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%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main changes: connecting a backend via tRPC and implementing a homepage group search feature, which aligns with the primary modifications across all files in the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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.

@lorenzocorallo lorenzocorallo requested a review from BIA3IA May 29, 2026 21:42
Copy link
Copy Markdown
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: 3

🧹 Nitpick comments (2)
src/queries/groups.ts (1)

5-8: ⚡ Quick win

Validate/clamp Server Action inputs.

"use server" exposes searchGroups as a public endpoint, so query and limit arrive directly from the client and are forwarded unchecked. The UI passes 20, but a direct caller can supply any limit, potentially triggering heavy backend queries. Consider validating with Zod (already a dependency) and clamping limit, unless the backend strictly enforces these bounds.

🛡️ Example validation
 "use server"

 import { trpc } from "`@/lib/backend`"
+import { z } from "zod"
+
+const schema = z.object({
+  query: z.string().max(200),
+  limit: z.number().int().min(1).max(50),
+})

-export async function searchGroups(query: string, limit: number = 6) {
-  const res = await trpc.tg.groups.search.query({ query, limit })
-  return res
+export async function searchGroups(query: string, limit: number = 6) {
+  const input = schema.parse({ query, limit })
+  return await trpc.tg.groups.search.query(input)
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/queries/groups.ts` around lines 5 - 8, searchGroups is exposed as a
server action and forwards unvalidated client inputs directly to
trpc.tg.groups.search.query; validate and clamp inputs before calling the
backend: use Zod to parse/validate the incoming query (trim, ensure non-empty
string) and parse limit as an integer with a min (e.g. 1) and a sensible max
(e.g. 50), then pass the validated/clamped values to trpc.tg.groups.search.query
(reference: function searchGroups and the downstream call
trpc.tg.groups.search.query).
package.json (1)

27-27: 💤 Low value

Remove unused @trpc/next dependency (if not needed)

@trpc/next is only present in package.json and there are no repo references to @trpc/next / createTRPCNext. src/lib/backend.ts uses createTRPCClient + httpBatchLink from @trpc/client, so @trpc/next (React Query/createTRPCNext) doesn’t appear to be used—drop it unless something else depends on it indirectly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@package.json` at line 27, Dependency `@trpc/next` appears unused—search the
repo for any imports/usages of "`@trpc/next`" or createTRPCNext (and confirm
src/lib/backend.ts only uses createTRPCClient and httpBatchLink from
`@trpc/client`); if there are no references, remove the "`@trpc/next`" entry from
package.json, run package manager install to update lockfile, and run tests/lint
to ensure nothing else depended on it indirectly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/home/group-search.tsx`:
- Line 57: The Link element rendering group links (the one using
key={g.telegramId} href={g.link!} target="_blank"}) lacks rel attributes for
safety; update that Link (in the group-search component) to include
rel="noopener noreferrer" when target="_blank" is present so external links use
rel="noopener noreferrer" to prevent reverse-tabnabbing and referrer leakage.
- Around line 53-72: The branch currently checks results.count > 0 but then
filters groups by g.link, which can produce an empty render; update GroupSearch
to first compute a filteredGroups array (e.g., const filteredGroups =
results?.groups.filter(g => g.link) ?? []) and use filteredGroups.length > 0 for
the conditional render, map over filteredGroups (using g.telegramId, g.link,
g.title), and keep the existing debouncedSearch.state.isLoading fallback so the
spinner or "No group found" message shows when filteredGroups is empty.

In `@src/lib/backend.ts`:
- Around line 11-14: The httpBatchLink configuration in backend.ts currently
omits a fetch option, leaving requests unbounded; update the httpBatchLink call
(the object that currently has url and transformer: SuperJSON) to pass a fetch
ponyfill that uses AbortController and setTimeout to enforce a timeout: create a
fetch wrapper that creates an AbortController, starts a timer (clear on
success), calls the real fetch with signal, and rejects/aborts when the timer
fires, then supply that wrapper as the fetch property to httpBatchLink so
backend requests time out safely.

---

Nitpick comments:
In `@package.json`:
- Line 27: Dependency `@trpc/next` appears unused—search the repo for any
imports/usages of "`@trpc/next`" or createTRPCNext (and confirm src/lib/backend.ts
only uses createTRPCClient and httpBatchLink from `@trpc/client`); if there are no
references, remove the "`@trpc/next`" entry from package.json, run package manager
install to update lockfile, and run tests/lint to ensure nothing else depended
on it indirectly.

In `@src/queries/groups.ts`:
- Around line 5-8: searchGroups is exposed as a server action and forwards
unvalidated client inputs directly to trpc.tg.groups.search.query; validate and
clamp inputs before calling the backend: use Zod to parse/validate the incoming
query (trim, ensure non-empty string) and parse limit as an integer with a min
(e.g. 1) and a sensible max (e.g. 50), then pass the validated/clamped values to
trpc.tg.groups.search.query (reference: function searchGroups and the downstream
call trpc.tg.groups.search.query).
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: d7a5d4fc-29f6-4e62-9de1-e29d5963de91

📥 Commits

Reviewing files that changed from the base of the PR and between 1080199 and 87109a7.

⛔ Files ignored due to path filters (2)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
  • src/assets/icons/telegram-fill.svg is excluded by !**/*.svg
📒 Files selected for processing (9)
  • .env.example
  • package.json
  • src/components/home/group-search.tsx
  • src/components/home/hero.tsx
  • src/components/spinner.tsx
  • src/env.js
  • src/lib/backend.ts
  • src/queries/groups.ts
  • src/types.ts

Comment on lines +53 to +72
{results && results.count > 0 ? (
results?.groups
.filter((g) => g.link)
.map((g) => (
<Link key={g.telegramId} href={g.link!} target="_blank">
<div className="flex items-center justify-start gap-3 px-4 py-3 text-start hover:bg-background-blur">
<Image key="telegram" src={telegram} alt="Telegram" className="size-5" />
{g.title}
</div>
</Link>
))
) : (
<div className="flex h-12 items-center justify-center px-4">
{debouncedSearch.state.isLoading ? (
<Spinner className="fill-blue-primary text-blue-primary/20" />
) : (
"No group found"
)}
</div>
)}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Edge case: count > 0 but no group has a link renders an empty dropdown.

The branch chooses the results list whenever results.count > 0, but the list is then filtered by g.link. If results exist yet none have a link, the mapped output is empty and neither the spinner nor the "No group found" message is shown — the user sees a blank panel. Base the branch on the filtered count instead.

🐛 Suggested fix
-            {results && results.count > 0 ? (
-              results?.groups
-                .filter((g) => g.link)
-                .map((g) => (
+            {(() => {
+              const linked = results?.groups.filter((g) => g.link) ?? []
+              return results && linked.length > 0 ? (
+                linked.map((g) => (
                   <Link key={g.telegramId} href={g.link!} target="_blank">
                     <div className="flex items-center justify-start gap-3 px-4 py-3 text-start hover:bg-background-blur">
                       <Image key="telegram" src={telegram} alt="Telegram" className="size-5" />
                       {g.title}
                     </div>
                   </Link>
-                ))
-            ) : (
+                ))
+              ) : (
               <div className="flex h-12 items-center justify-center px-4">
                 {debouncedSearch.state.isLoading ? (
                   <Spinner className="fill-blue-primary text-blue-primary/20" />
                 ) : (
                   "No group found"
                 )}
               </div>
-            )}
+              )
+            })()}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
{results && results.count > 0 ? (
results?.groups
.filter((g) => g.link)
.map((g) => (
<Link key={g.telegramId} href={g.link!} target="_blank">
<div className="flex items-center justify-start gap-3 px-4 py-3 text-start hover:bg-background-blur">
<Image key="telegram" src={telegram} alt="Telegram" className="size-5" />
{g.title}
</div>
</Link>
))
) : (
<div className="flex h-12 items-center justify-center px-4">
{debouncedSearch.state.isLoading ? (
<Spinner className="fill-blue-primary text-blue-primary/20" />
) : (
"No group found"
)}
</div>
)}
{(() => {
const linked = results?.groups.filter((g) => g.link) ?? []
return results && linked.length > 0 ? (
linked.map((g) => (
<Link key={g.telegramId} href={g.link!} target="_blank">
<div className="flex items-center justify-start gap-3 px-4 py-3 text-start hover:bg-background-blur">
<Image key="telegram" src={telegram} alt="Telegram" className="size-5" />
{g.title}
</div>
</Link>
))
) : (
<div className="flex h-12 items-center justify-center px-4">
{debouncedSearch.state.isLoading ? (
<Spinner className="fill-blue-primary text-blue-primary/20" />
) : (
"No group found"
)}
</div>
)
})()}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/home/group-search.tsx` around lines 53 - 72, The branch
currently checks results.count > 0 but then filters groups by g.link, which can
produce an empty render; update GroupSearch to first compute a filteredGroups
array (e.g., const filteredGroups = results?.groups.filter(g => g.link) ?? [])
and use filteredGroups.length > 0 for the conditional render, map over
filteredGroups (using g.telegramId, g.link, g.title), and keep the existing
debouncedSearch.state.isLoading fallback so the spinner or "No group found"
message shows when filteredGroups is empty.

Comment thread src/components/home/group-search.tsx Outdated
results?.groups
.filter((g) => g.link)
.map((g) => (
<Link key={g.telegramId} href={g.link!} target="_blank">
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Add rel="noopener noreferrer" to external target="_blank" links.

External links opening in a new tab should set rel to prevent reverse-tabnabbing and referrer leakage.

🛡️ Suggested fix
-                  <Link key={g.telegramId} href={g.link!} target="_blank">
+                  <Link key={g.telegramId} href={g.link!} target="_blank" rel="noopener noreferrer">
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<Link key={g.telegramId} href={g.link!} target="_blank">
<Link key={g.telegramId} href={g.link!} target="_blank" rel="noopener noreferrer">
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/home/group-search.tsx` at line 57, The Link element rendering
group links (the one using key={g.telegramId} href={g.link!} target="_blank"})
lacks rel attributes for safety; update that Link (in the group-search
component) to include rel="noopener noreferrer" when target="_blank" is present
so external links use rel="noopener noreferrer" to prevent reverse-tabnabbing
and referrer leakage.

Comment thread src/lib/backend.ts
Comment on lines +11 to +14
httpBatchLink({
url,
transformer: SuperJSON,
}),
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show the relevant portion of src/lib/backend.ts
if [ -f src/lib/backend.ts ]; then
  echo "=== src/lib/backend.ts (lines 1-120) ==="
  nl -ba src/lib/backend.ts | sed -n '1,120p'
else
  echo "File src/lib/backend.ts not found"
fi

# Find all uses of httpBatchLink
echo "=== rg httpBatchLink ==="
rg -n "httpBatchLink\s*\(" -S . || true

# Find any fetch/AbortController/timeout patterns related to TRPC or backend calls
echo "=== rg AbortController|timeout|signal: controller.signal|fetch\\(url,|fetch\\s*:\\s*|request timeout ==="
rg -n "AbortController|controller\.abort|signal:\s*controller\.signal|timeout\s*=|setTimeout\(|request timeout|fetch\\(url,|fetch\\s*:\\s*\\(" -S . || true

# Show package.json deps for context (optional)
if [ -f package.json ]; then
  echo "=== package.json (deps excerpt) ==="
  nl -ba package.json | sed -n '1,200p'
fi

Repository: PoliNetworkOrg/web

Length of output: 164


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Show first ~160 lines with line numbers
if [ -f src/lib/backend.ts ]; then
  echo "=== src/lib/backend.ts (lines 1-160) ==="
  (cat -n src/lib/backend.ts | sed -n '1,160p') || true
else
  echo "File src/lib/backend.ts not found"
fi

echo "=== rg httpBatchLink ==="
rg -n "httpBatchLink\s*\(" -S . || true

echo "=== rg AbortController|controller\.abort|signal:\s*controller\.signal|setTimeout\(|request timeout|fetch\\(url, ==="
rg -n "AbortController|controller\.abort|signal:\s*controller\.signal|setTimeout\(|request timeout|fetch\(url," -S . || true

if [ -f package.json ]; then
  echo "=== package.json (deps excerpt) ==="
  (cat -n package.json | sed -n '1,220p') || true
fi

Repository: PoliNetworkOrg/web

Length of output: 3061


🌐 Web query:

@trpc/client httpBatchLink fetch option signature v11

💡 Result:

In tRPC v11, the fetch option for httpBatchLink is defined within the HTTPLinkOptions interface [1][2]. Its signature is: fetch?: typeof fetch [1][2] This option allows you to provide a custom implementation (or "ponyfill") of the fetch API [1][2]. It must be compliant with the standard global fetch function signature [3]. When configuring httpBatchLink in v11, note that several properties have moved. Specifically, if you use a data transformer (like superjson), it must now be passed directly into the httpBatchLink options object rather than at the root of the client configuration [4][5]. Example usage: httpBatchLink({ url: 'http://localhost:3000', fetch: myCustomFetch, // Optional custom fetch implementation transformer: superjson, })

Citations:


Add an AbortController timeout to httpBatchLink backend requests.

src/lib/backend.ts currently configures httpBatchLink with only url and transformer, so it uses the default fetch behavior with no upper-bound on how long a stalled backend call can take. Add a fetch ponyfill using AbortController/setTimeout to bound request time (tRPC v11 supports fetch?: typeof fetch on httpBatchLink options).

Suggested fix
   links: [
     httpBatchLink({
       url,
+      fetch(url, options) {
+        const controller = new AbortController()
+        const timeout = setTimeout(() => controller.abort(), 8000)
+        return fetch(url, { ...options, signal: controller.signal }).finally(() =>
+          clearTimeout(timeout),
+        )
+      },
       transformer: SuperJSON,
     }),
   ],
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/lib/backend.ts` around lines 11 - 14, The httpBatchLink configuration in
backend.ts currently omits a fetch option, leaving requests unbounded; update
the httpBatchLink call (the object that currently has url and transformer:
SuperJSON) to pass a fetch ponyfill that uses AbortController and setTimeout to
enforce a timeout: create a fetch wrapper that creates an AbortController,
starts a timer (clear on success), calls the real fetch with signal, and
rejects/aborts when the timer fires, then supply that wrapper as the fetch
property to httpBatchLink so backend requests time out safely.

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.

1 participant