feat: connect backend via trpc, homepage group search#123
feat: connect backend via trpc, homepage group search#123lorenzocorallo wants to merge 3 commits into
Conversation
|
Warning Review limit reached
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 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 configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughThis 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. ChangestRPC Group Search Feature
🚥 Pre-merge checks | ✅ 3 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (3 passed)
✏️ 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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
src/queries/groups.ts (1)
5-8: ⚡ Quick winValidate/clamp Server Action inputs.
"use server"exposessearchGroupsas a public endpoint, soqueryandlimitarrive directly from the client and are forwarded unchecked. The UI passes20, but a direct caller can supply anylimit, potentially triggering heavy backend queries. Consider validating with Zod (already a dependency) and clampinglimit, 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 valueRemove unused
@trpc/nextdependency (if not needed)
@trpc/nextis only present inpackage.jsonand there are no repo references to@trpc/next/createTRPCNext.src/lib/backend.tsusescreateTRPCClient+httpBatchLinkfrom@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
⛔ Files ignored due to path filters (2)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlsrc/assets/icons/telegram-fill.svgis excluded by!**/*.svg
📒 Files selected for processing (9)
.env.examplepackage.jsonsrc/components/home/group-search.tsxsrc/components/home/hero.tsxsrc/components/spinner.tsxsrc/env.jssrc/lib/backend.tssrc/queries/groups.tssrc/types.ts
| {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> | ||
| )} |
There was a problem hiding this comment.
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.
| {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.
| results?.groups | ||
| .filter((g) => g.link) | ||
| .map((g) => ( | ||
| <Link key={g.telegramId} href={g.link!} target="_blank"> |
There was a problem hiding this comment.
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.
| <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.
| httpBatchLink({ | ||
| url, | ||
| transformer: SuperJSON, | ||
| }), |
There was a problem hiding this comment.
🧩 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'
fiRepository: 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
fiRepository: 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:
- 1: https://github.com/trpc/trpc/blob/main/www/docs/client/links/httpBatchLink.md
- 2: https://trpc.io/docs/client/links/httpBatchLink
- 3: feat: ability to get information about operation(s) from fetch config trpc/trpc#4184
- 4: trpc v11 error: The transformer property has moved to httpLink/httpBatchLink/wsLink trpc/trpc#5570
- 5: https://trpc.io/docs/migrate-from-v10-to-v11
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.
No description provided.