Skip to content

feat(web): set up tRPC v11 API layer with React Query v5#41

Merged
GRACENOBLE merged 2 commits into
mainfrom
trpc
Jun 24, 2026
Merged

feat(web): set up tRPC v11 API layer with React Query v5#41
GRACENOBLE merged 2 commits into
mainfrom
trpc

Conversation

@GRACENOBLE

@GRACENOBLE GRACENOBLE commented Jun 24, 2026

Copy link
Copy Markdown
Owner

Closes #36

Summary

  • Installs tRPC v11 + React Query v5 + Zod and wires the full API layer into the Next.js App Router app
  • publicProcedure proxies to the Go backend; protectedProcedure enforces auth via Bearer token or __session cookie
  • TRPCProvider / QueryClientProvider wired into the root layout via app/providers.tsx
  • Server-side caller (createServerCaller) available for use in Server Components
  • Pins postgres:16 in backend/docker-compose.yml

Test plan

  • pnpm test — 51 tests pass (includes health router and protectedProcedure unit tests)
  • pnpm lint — 0 errors
  • pnpm build — clean TypeScript + static generation, /api/trpc/[trpc] route is dynamic

Summary by CodeRabbit

  • New Features

    • Integrated tRPC and React Query for type-safe client-server communication
    • Created home page with Hero and About sections
    • Added health check endpoint for backend status monitoring
    • Implemented protected API endpoints for authentication and notifications
  • Documentation

    • Added comprehensive tRPC integration guide
    • Updated component conventions and structure
    • Enhanced styling documentation for Tailwind CSS v4
    • Expanded data-fetching patterns documentation
  • Chores

    • Upgraded PostgreSQL to v16
    • Added tRPC, React Query, and Zod dependencies

Closes #36

- Install @trpc/server, @trpc/client, @trpc/react-query, @tanstack/react-query, zod
- Scaffold server/trpc.ts: TRPCContext, publicProcedure, protectedProcedure (Bearer/cookie auth), createCallerFactory
- Add routers: health (proxies Go backend), auth (session/signOut stubs), notifications (FCM token stub)
- Wire /api/trpc/[trpc] fetchRequestHandler (GET + POST)
- Add lib/trpc/client.tsx: TRPCProvider wrapping QueryClientProvider
- Add lib/trpc/server.ts: cached createServerCaller for Server Components
- Wire TRPCProvider via app/providers.tsx into root layout
- Add Vitest tests for health router and protectedProcedure (UNAUTHORIZED, Bearer, cookie)
- Pin postgres:16 in docker-compose.yml
- Update docs: styling.md and components.md (shadcn/ui), data-fetching.md (tRPC section), new trpc.md
@github-actions github-actions Bot added area: backend Go REST API area: web Next.js web app type: chore Cleanup or maintenance tasks labels Jun 24, 2026
@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

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

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

⌛ 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.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, the refill rate gradually slows as usage increases. The highest same-day bursts are limited more strictly.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 46be352f-60c3-4a83-b647-bc924a7efe73

📥 Commits

Reviewing files that changed from the base of the PR and between 2c30572 and 2fa6b59.

📒 Files selected for processing (9)
  • backend/internal/infrastructure/database/postgres/health_repository_test.go
  • web/lib/trpc/__tests__/server.test.ts
  • web/lib/trpc/__tests__/utils.test.ts
  • web/lib/trpc/client.tsx
  • web/lib/trpc/utils.ts
  • web/server/routers/__tests__/auth.test.ts
  • web/server/routers/__tests__/health.test.ts
  • web/server/routers/health.ts
  • web/server/trpc.ts
📝 Walkthrough

Walkthrough

Adds a full tRPC v11 + React Query v5 API layer to the Next.js frontend. This includes tRPC server initialization, publicProcedure/protectedProcedure, three routers (health, auth, notifications), a Next.js API route handler, typed React client, server-side caller, root layout provider wiring, stub home page components, Vitest tests, and comprehensive documentation. The backend postgres image is also pinned to version 16.

Changes

tRPC API Layer

Layer / File(s) Summary
tRPC server context and procedure contracts
web/package.json, web/server/trpc.ts
Adds @trpc/*, @tanstack/react-query, and zod dependencies. Defines TRPCContext, createTRPCContext, publicProcedure, and protectedProcedure middleware that checks for a Bearer token or __session cookie and throws UNAUTHORIZED when absent.
Router definitions
web/server/routers/health.ts, web/server/routers/auth.ts, web/server/routers/notifications.ts, web/server/routers/_app.ts
Creates healthRouter (public query proxying GET /health to the Go backend), authRouter (protected stubs: session query, signOut mutation), notificationsRouter (protected stubs: registerFcmToken with Zod validation, list), and assembles all three into appRouter with exported AppRouter type.
API route handler and client/server callers
web/app/api/trpc/[trpc]/route.ts, web/lib/trpc/client.tsx, web/lib/trpc/server.ts
Adds the fetchRequestHandler-backed Next.js route exported for GET and POST. Creates the typed trpc React client with httpBatchLink and getBaseUrl(), the TRPCProvider component with memoized QueryClient, and the cached createServerCaller for server components using forwarded request headers.
App layout wiring and home page scaffold
web/app/providers.tsx, web/app/layout.tsx, web/app/page.tsx, web/components/home/*
Adds a 'use client' Providers wrapper around TRPCProvider, updates the root layout <body> to render children inside <Providers>, adds stub Hero and About section components, and composes them in page.tsx.
Vitest tests for health and auth procedures
web/server/routers/__tests__/health.test.ts, web/server/routers/__tests__/auth.test.ts
Tests healthRouter.query() with a stubbed global fetch (success path and 503 error path), and tests protectedProcedure via authRouter.session() (no credentials → UNAUTHORIZED, Bearer token → authenticated, __session cookie → authenticated).
Documentation
web/docs/trpc.md, web/docs/data-fetching.md, web/docs/components.md, web/docs/styling.md, web/docs/_index.md, web/AGENTS.md
Adds docs/trpc.md covering the full tRPC setup, procedures, routers, client/server usage, layout wiring, usage examples, extension patterns, and testing. Updates data-fetching.md with a tRPC section. Refreshes components.md with directory structure and updated conventions. Updates styling.md with Tailwind v4 CSS-first guidance. Updates doc indexes.

Postgres Version Pin

Layer / File(s) Summary
Pin postgres image to version 16
backend/docker-compose.yml
Changes the psql_bp service image tag from postgres:latest to postgres:16.

Sequence Diagram

sequenceDiagram
  participant Browser
  participant TRPCProvider
  participant NextjsAPIRoute
  participant appRouter
  participant GoBackend

  rect rgba(99, 102, 241, 0.5)
    note over Browser, TRPCProvider: Client Component path
    Browser->>TRPCProvider: trpc.health.useQuery()
    TRPCProvider->>NextjsAPIRoute: POST /api/trpc/health.query (httpBatchLink)
    NextjsAPIRoute->>appRouter: fetchRequestHandler → health.query()
    appRouter->>GoBackend: fetch BACKEND_URL/health
    GoBackend-->>appRouter: { status, database }
    appRouter-->>NextjsAPIRoute: JSON result
    NextjsAPIRoute-->>TRPCProvider: response
    TRPCProvider-->>Browser: React Query state (data/isLoading/isError)
  end

  rect rgba(16, 185, 129, 0.5)
    note over Browser, appRouter: Server Component path
    Browser->>appRouter: createServerCaller() → caller.health.query()
    appRouter->>GoBackend: fetch BACKEND_URL/health
    GoBackend-->>appRouter: { status, database }
    appRouter-->>Browser: typed result (no HTTP round-trip)
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • #36 feat(web): set up tRPC API layer with React Query — This PR directly implements all acceptance criteria from that issue: the /api/trpc/[trpc] route, publicProcedure/protectedProcedure, health router with Vitest tests, protectedProcedure UNAUTHORIZED unit tests, client/server tRPC usage, and documentation in data-fetching.md.
  • #37 — This PR introduces the protectedProcedure middleware, authRouter stubs (session, signOut), and the Bearer/cookie authorization check that issue #37 (NextAuth.js v5 integration) depends on.

Suggested labels

area: web

🐇 Hoppity-hop, the types align,
tRPC procedures in a row so fine!
Protected routes guard the session door,
React Query caches, who could ask for more?
The health router pings the Go backend with glee,
And zod validates — type-safe as can be! 🎉

🚥 Pre-merge checks | ✅ 4 | ❌ 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 (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(web): set up tRPC v11 API layer with React Query v5' is specific and accurately summarizes the main change—implementing a complete tRPC and React Query integration for the web application.
Linked Issues check ✅ Passed The PR fully satisfies all linked issue #36 requirements: tRPC v11 + React Query v5 integration with proper architecture (trpc.ts, routers, API route handler, client setup, server caller), publicProcedure proxying to backend, protectedProcedure authorization enforcement, health router example with tests, and comprehensive documentation.
Out of Scope Changes check ✅ Passed All code changes align with the PR objectives of establishing tRPC/React Query infrastructure; minor changes like PostgreSQL version pin and documentation updates are supporting changes directly related to the primary feature.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch trpc

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 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 `@backend/docker-compose.yml`:
- Line 3: Update the Postgres version in the testcontainer setup and
documentation to match the pinned version in docker-compose.yml. Locate the
testcontainer configuration in health_repository_test.go and the testing
documentation, and change any references from postgres:latest to postgres:16.
This ensures consistency across docker-compose.yml, the testcontainer setup, and
the documentation to prevent environment drift and avoid hiding version-specific
database behavior during testing.

In `@web/lib/trpc/client.tsx`:
- Around line 11-34: Create a Vitest test file for web/lib/trpc/client.tsx to
add unit test coverage for the new getBaseUrl function and TRPCProvider
component. For getBaseUrl, write tests to verify the correct base URL is
returned in different environments: when running in the browser (returns empty
string), when VERCEL_URL environment variable is set (returns https URL), and
when running locally without VERCEL_URL (returns localhost). For TRPCProvider,
write a test to verify it renders children correctly and sets up the TRPC and
QueryClient providers properly.

In `@web/lib/trpc/server.ts`:
- Around line 9-17: The `createServerCaller` function lacks required Vitest unit
tests. Create a new test file (typically `server.test.ts` or `server.spec.ts`)
in `web/lib/trpc/` and write tests that validate header propagation from the
request object into the context, verify that the TRPC caller is correctly
instantiated with the created context, and test edge cases such as missing or
empty headers. Ensure the tests use Vitest's standard patterns and cover the
main code path of `createServerCaller` to confirm both header handling and
context/caller creation work as expected.

In `@web/server/routers/__tests__/health.test.ts`:
- Around line 6-8: The global fetch stub created with vi.stubGlobal('fetch',
mockFetch) is not being restored after the tests run, which causes the mock to
persist and affect other tests. Add an afterEach or afterAll hook that calls
vi.unstubAllGlobals() or explicitly restores the fetch function to ensure the
stub is cleaned up between tests and does not leak into other test suites.

In `@web/server/routers/health.ts`:
- Around line 7-8: The fetch call to `${BACKEND_URL}/health` in the health check
handler lacks a timeout mechanism, which can cause the request to hang
indefinitely if the backend is unresponsive. Add a timeout by using an
AbortController: create an AbortController instance, set a setTimeout to abort
the controller after a reasonable duration (such as 5-10 seconds), and pass the
controller's signal in the fetch options. This ensures the request will be
terminated automatically if the backend fails to respond within the timeout
period.

In `@web/server/trpc.ts`:
- Around line 23-27: The hasBearer check in protectedProcedure only validates
that the authHeader starts with 'Bearer ' but does not verify that an actual
token exists after the prefix, allowing empty tokens to pass. Similarly, the
hasSessionCookie check uses a simple substring match that does not validate an
actual session value is present. Strengthen both checks by: for hasBearer,
verify that there is a non-empty token string following the 'Bearer ' prefix
(not just the prefix itself), and for hasSessionCookie, properly parse the
cookie to confirm that __session has an assigned value rather than just checking
for the substring '__session=' in the cookie header.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 2f4def12-9880-4120-b23f-a905cbe95966

📥 Commits

Reviewing files that changed from the base of the PR and between b471f47 and 2c30572.

⛔ Files ignored due to path filters (1)
  • web/pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (23)
  • backend/docker-compose.yml
  • web/AGENTS.md
  • web/app/api/trpc/[trpc]/route.ts
  • web/app/layout.tsx
  • web/app/page.tsx
  • web/app/providers.tsx
  • web/components/home/about.tsx
  • web/components/home/hero.tsx
  • web/docs/_index.md
  • web/docs/components.md
  • web/docs/data-fetching.md
  • web/docs/styling.md
  • web/docs/trpc.md
  • web/lib/trpc/client.tsx
  • web/lib/trpc/server.ts
  • web/package.json
  • web/server/routers/__tests__/auth.test.ts
  • web/server/routers/__tests__/health.test.ts
  • web/server/routers/_app.ts
  • web/server/routers/auth.ts
  • web/server/routers/health.ts
  • web/server/routers/notifications.ts
  • web/server/trpc.ts

Comment thread backend/docker-compose.yml
Comment thread web/lib/trpc/client.tsx Outdated
Comment thread web/lib/trpc/server.ts
Comment thread web/server/routers/__tests__/health.test.ts
Comment thread web/server/routers/health.ts Outdated
Comment thread web/server/trpc.ts Outdated
- protectedProcedure: reject empty Bearer tokens and match __session
  cookie by exact key (prevents substring-in-value spoofing)
- health router: add 5 s AbortSignal timeout to backend fetch
- health.test.ts: unstub global fetch in afterAll to prevent leakage;
  update fetch assertion to include options argument
- auth.test.ts: add edge-case tests for empty Bearer, empty cookie
  value, and __session= appearing inside another cookie's value
- Extract getBaseUrl to lib/trpc/utils.ts; add @vitest-environment node
  unit tests covering browser, Vercel, and localhost branches
- Add lib/trpc/__tests__/server.test.ts: unit tests for createServerCaller
  with mocked next/headers and react.cache
- backend: pin testcontainers postgres image to postgres:16
@GRACENOBLE GRACENOBLE merged commit 290ca07 into main Jun 24, 2026
4 checks passed
@GRACENOBLE GRACENOBLE deleted the trpc branch June 24, 2026 00:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area: backend Go REST API area: web Next.js web app type: chore Cleanup or maintenance tasks

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(web): set up tRPC API layer with React Query

1 participant