TRPC configured Successfully - #7
Conversation
WalkthroughThis PR integrates tRPC and React Query into the web app, establishing a new TRPC package with a typed router and API route handler. It configures client-side providers in the root layout and updates the page component to use tRPC queries. Changes
Sequence Diagram(s)sequenceDiagram
participant Client as Web Client
participant Layout as RootLayout
participant TRPCProvider as TRPC Provider
participant QueryProvider as QueryClient Provider
participant Page as Home Page
participant APIRoute as /api/trpc Route
participant Router as TRPC Router
Client->>Layout: Load page
Layout->>Layout: Initialize QueryClient & TRPC client
Layout->>TRPCProvider: Wrap with TRPC provider
TRPCProvider->>QueryProvider: Wrap with QueryClient provider
QueryProvider->>Client: Render children
Client->>Page: Render Home component
Page->>Page: Call trpc.greeting.useQuery()
Page->>APIRoute: POST /api/trpc (greeting query)
APIRoute->>Router: Invoke greeting procedure
Router->>Router: Validate input { name: string }
Router-->>APIRoute: Return greeting response
APIRoute-->>Page: Return query result
Page->>Client: Render greeting with data
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
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: 7
🧹 Nitpick comments (6)
apps/web/app/utils/trpc.ts (1)
5-5: Consider removing redundant type annotation.The explicit type annotation
ReturnType<typeof createTRPCReact<AppRouter>>is unnecessary as TypeScript can infer it from the initialization.Apply this diff for cleaner code:
-export const trpc: ReturnType<typeof createTRPCReact<AppRouter>> = createTRPCReact<AppRouter>(); +export const trpc = createTRPCReact<AppRouter>();apps/web/app/page.tsx (1)
6-7: Remove outdated comments.The comments on lines 6-7 appear to be debugging/development notes that are no longer needed. They should be removed for cleaner code.
Apply this diff:
- // Fix: Use a valid tRPC query (replace 'greeting' with existing query or handle error gracefully) - // Use the valid 'greeting' query instead of 'hello' const { data, isLoading, error } = trpc.greeting.useQuery({ name: 'tRPC' });apps/web/app/api/trpc/[trpc]/route.ts (1)
5-11: LGTM! Consider expanding context for future needs.The tRPC route handler is correctly implemented. The empty context function is acceptable for the initial setup.
For future enhancements, consider populating the context with authentication data, user sessions, or request metadata:
createContext: async ({ req }) => ({ // session: await getSession(req), // userId: await getUserId(req), })packages/trpc/src/index.ts (1)
17-17: Remove commented code.The commented-out type export is no longer needed since
packages/trpc/src/AppRouter.tsnow handles this export.Apply this diff:
-// Export the AppRouter type for client-side usage -// export type AppRouter = typeof appRouter;apps/web/app/layout.tsx (1)
16-24: Consider adding QueryClient configuration and head tag.Two recommendations for production readiness:
The HTML structure is missing a
<head>tag, which is typically needed for metadata, title, and other head elements in Next.js layouts.The QueryClient is created without configuration options. Consider adding production-appropriate defaults:
- const [queryClient] = useState(() => new QueryClient()); + const [queryClient] = useState(() => new QueryClient({ + defaultOptions: { + queries: { + staleTime: 60 * 1000, // 1 minute + refetchOnWindowFocus: false, + }, + }, + }));<html lang="en"> + <head /> <body>packages/trpc/package.json (1)
7-7: Remove or implement the test script.The test script is a placeholder that exits with code 1, which will cause CI pipeline failures if test commands are run.
Consider either removing the script or adding actual tests:
- "test": "echo \"Error: no test specified\" && exit 1",Or implement tests:
- "test": "echo \"Error: no test specified\" && exit 1", + "test": "vitest run",
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (11)
apps/web/app/api/trpc/[trpc]/route.ts(1 hunks)apps/web/app/layout.tsx(1 hunks)apps/web/app/page.tsx(1 hunks)apps/web/app/utils/trpc.ts(1 hunks)apps/web/package.json(1 hunks)apps/web/tsconfig.json(2 hunks)packages/trpc/package.json(1 hunks)packages/trpc/src/AppRouter.ts(1 hunks)packages/trpc/src/index.ts(1 hunks)packages/trpc/tsconfig.json(1 hunks)packages/trpc/tsconfig.tsbuildinfo(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (5)
apps/web/app/utils/trpc.ts (1)
packages/trpc/src/AppRouter.ts (1)
AppRouter(4-4)
apps/web/app/layout.tsx (1)
apps/web/app/utils/trpc.ts (1)
trpc(5-5)
apps/web/app/api/trpc/[trpc]/route.ts (1)
packages/trpc/src/index.ts (1)
appRouter(8-14)
apps/web/app/page.tsx (1)
apps/web/app/utils/trpc.ts (1)
trpc(5-5)
packages/trpc/src/AppRouter.ts (1)
packages/trpc/src/index.ts (1)
appRouter(8-14)
🔇 Additional comments (6)
packages/trpc/tsconfig.json (1)
1-7: LGTM! TypeScript configuration is correct.The configuration properly extends the base config and sets appropriate compilation directories for the TRPC package.
packages/trpc/src/AppRouter.ts (1)
1-4: LGTM! Proper type export pattern for tRPC.The type alias correctly exposes the router type for client-side type inference, following tRPC best practices.
apps/web/app/page.tsx (1)
8-14: LGTM! Proper tRPC query implementation.The component correctly uses the tRPC hook with appropriate loading, error, and data state handling.
apps/web/app/layout.tsx (2)
1-1: Client-side root layout impacts SSR.Making the root layout a client component disables server-side rendering for the entire app structure. This is necessary for tRPC and React Query providers, but be aware that this means no layout-level SSR benefits. Consider whether any layout elements could be extracted to server components if SSR is important for your use case.
16-24: The provider structure in the code is correct per tRPC v11 documentation.tRPC v11's correct pattern requires wrapping the app with TRPCProvider (passing both client and queryClient) and then QueryClientProvider with the same queryClient instance. The code under review follows this exact pattern and requires no changes.
Likely an incorrect or invalid review comment.
apps/web/package.json (1)
18-18: The review comment is incorrect and would break the codebase.The @trpc/react-query package is still supported by tRPC—it's not deprecated, merely superseded. More critically, the code actively imports from this package:
apps/web/app/utils/trpc.ts:2usescreateTRPCReactfrom@trpc/react-query. Removing the package frompackage.jsonwithout updating this import would cause the application to fail.If the intent is to migrate to
@trpc/tanstack-react-query, both the import inapps/web/app/utils/trpc.tsand the package.json entry must be updated together.Likely an incorrect or invalid review comment.
| export default function RootLayout({ children }: { children: React.ReactNode }) { | ||
| const [queryClient] = useState(() => new QueryClient()); | ||
| const [trpcClient] = useState(() => | ||
| trpc.createClient({ | ||
| links: [httpBatchLink({ url: 'http://localhost:3000/api/trpc' })], | ||
| }), | ||
| ); |
There was a problem hiding this comment.
Replace hardcoded localhost URL with environment variable.
The hardcoded http://localhost:3000/api/trpc URL will break in production or any non-local environment.
Apply this diff to use an environment variable:
const [trpcClient] = useState(() =>
trpc.createClient({
- links: [httpBatchLink({ url: 'http://localhost:3000/api/trpc' })],
+ links: [httpBatchLink({ url: process.env.NEXT_PUBLIC_TRPC_URL || '/api/trpc' })],
}),
);Then add to your .env.local (for development):
NEXT_PUBLIC_TRPC_URL=http://localhost:3000/api/trpcAnd configure the production URL in your deployment environment.
🤖 Prompt for AI Agents
In apps/web/app/layout.tsx around lines 7 to 13, the trpc client is created with
a hardcoded 'http://localhost:3000/api/trpc'; replace this with
process.env.NEXT_PUBLIC_TRPC_URL (or a sensible fallback) so the URL is driven
by environment configuration; update the code to read the env var when creating
the client and add NEXT_PUBLIC_TRPC_URL to .env.local for development and set
the production URL in your deployment environment.
| "@trpc/tanstack-react-query": "^11.7.1", | ||
| "@workspace/ui": "workspace:*", | ||
| "client-only": "^0.0.1", | ||
| "fetch": "link:@trpc/server/adapters/fetch", |
There was a problem hiding this comment.
Remove incorrect "fetch" dependency.
The "fetch": "link:@trpc/server/adapters/fetch" dependency is incorrectly structured. The tRPC fetch adapter is not a separate package—it's a subpath export from @trpc/server. You don't need to declare it as a dependency.
Remove this line:
"client-only": "^0.0.1",
- "fetch": "link:@trpc/server/adapters/fetch",
"lucide-react": "^0.475.0",The fetch adapter should be imported directly in your code as:
import { fetchRequestHandler } from '@trpc/server/adapters/fetch';🤖 Prompt for AI Agents
In apps/web/package.json at line 23, remove the incorrect dependency entry
`"fetch": "link:@trpc/server/adapters/fetch",` because the tRPC fetch adapter is
not a separate package; delete that line from package.json and run your package
manager install to update lockfile; in code import the adapter via `import {
fetchRequestHandler } from '@trpc/server/adapters/fetch'` instead of declaring
it as a dependency.
| "react-dom": "^19.1.1" | ||
| "react-dom": "^19.1.1", | ||
| "server-only": "^0.0.1", | ||
| "trpc": "^0.11.3", |
There was a problem hiding this comment.
🧩 Analysis chain
Remove invalid "trpc" package dependency.
The package "trpc": "^0.11.3" doesn't exist on npm. All tRPC packages are scoped under @trpc/. This will cause installation failures.
This dependency should be removed as you already have the correct tRPC packages installed:
"server-only": "^0.0.1",
- "trpc": "^0.11.3",
"zod": "^3.25.76"Verify the package doesn't exist:
🏁 Script executed:
#!/bin/bash
# Check if 'trpc' package exists on npm
curl -s https://registry.npmjs.org/trpc | jq -r '.error // "Package exists"'Length of output: 14
[incorrect_review_comment]
The review comment is factually incorrect. The trpc package version 0.11.3 exists on npm (it's the latest version, last published 4 years ago), so installation will not fail. While the package has been deprecated in favor of @trpc/* packages, it remains a valid, installable dependency. The claim that the package doesn't exist and will cause installation failures is unfounded.
[incorrect_review_comment]
🌐 Web query:
trpc npm package version 0.11.3
💡 Result:
Do you mean the scoped tRPC packages (e.g. @trpc/server / @trpc/client)? tRPC uses scoped packages and current releases are in the 11.x series — there is no widely used "@trpc/*" package published as version 0.11.3 on npm. See the @trpc/server versions on npm and the tRPC docs. [1][2]
If you meant a different package name or need the exact publish info for a specific package/version, tell me the exact npm package name and I’ll look it up.
Sources:
[1] https://www.npmjs.com/package/%40trpc/server — versions & installs.
[2] https://trpc.io/docs/quickstart — tRPC v11 docs.
🏁 Script executed:
#!/bin/bash
# Check for all tRPC-related packages in apps/web/package.json
cat apps/web/package.json | jq '.dependencies | with_entries(select(.key | contains("trpc")))'Length of output: 189
Remove invalid "trpc" package dependency.
The unscoped "trpc": "^0.11.3" package is from an old package structure. All tRPC packages are now scoped under @trpc/* and are already present at v11.7.1. This dependency is redundant and incorrect and should be removed.
"server-only": "^0.0.1",
- "trpc": "^0.11.3",
"zod": "^3.25.76"📝 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.
| "trpc": "^0.11.3", |
🤖 Prompt for AI Agents
In apps/web/package.json around line 30, remove the invalid unscoped "trpc":
"^0.11.3" dependency; it is redundant and incorrect because current tRPC
packages are scoped under @trpc/* and already present. Update package.json by
deleting that "trpc" entry from the dependencies and then run npm/yarn install
(or update lockfile) to ensure the dependency graph is consistent.
| "license": "ISC", | ||
| "author": "", | ||
| "type": "commonjs", | ||
| "main": "index.js", |
There was a problem hiding this comment.
Fix inconsistent entry point with exports field.
The "main" field points to "index.js", but the exports field points to "./dist/index.js". This inconsistency can cause import resolution issues with older Node.js versions or tools that don't support the exports field.
Apply this diff:
- "main": "index.js",
+ "main": "./dist/index.js",📝 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.
| "main": "index.js", | |
| "main": "./dist/index.js", |
🤖 Prompt for AI Agents
In packages/trpc/package.json around line 5, the "main" field currently points
to "index.js" while the "exports" field points to "./dist/index.js", causing
inconsistent entry points; update the "main" field to "dist/index.js" (or remove
the "main" field if you prefer to rely solely on "exports") so both entry points
align with the built output.
| "scripts": { | ||
| "test": "echo \"Error: no test specified\" && exit 1" | ||
| "test": "echo \"Error: no test specified\" && exit 1", | ||
| "build": "tsc -b", | ||
| "dev": "node dist/index.js", | ||
| "start": "npm run build && npm run dev" | ||
| }, |
There was a problem hiding this comment.
🧩 Analysis chain
Verify the purpose of executable scripts in a library package.
The "dev" and "start" scripts run node dist/index.js, which is unusual for a library package. tRPC routers are typically imported by other applications rather than executed directly.
Please verify whether these scripts are intentional:
If this is meant to be a library only, consider removing the dev and start scripts. If it's meant to be executable, ensure there's a clear entry point and documentation.
🏁 Script executed:
#!/bin/bash
# Check if dist/index.js contains executable code (e.g., server startup)
cat packages/trpc/src/index.ts | head -30Length of output: 468
</script>
Remove executable scripts from library package.
The packages/trpc package exports a tRPC router and should have a "main" field pointing to the entry file for type imports, not executable scripts. The "dev" and "start" scripts that run node dist/index.js are inappropriate for a library package—they only export a router object, not executable code. Either add "main" to package.json and remove these scripts, or add Express/server code if this is meant to be a runnable server application.
🏁 Script executed:
#!/bin/bash
# Check if dist/index.js exists and what it contains
if [ -f packages/trpc/dist/index.js ]; then
echo "=== dist/index.js exists, first 20 lines ==="
head -20 packages/trpc/dist/index.js
else
echo "dist/index.js does not exist"
fi
# Also check package.json main/exports fields
echo ""
echo "=== package.json main/exports fields ==="
jq '.main, .exports, .bin' packages/trpc/package.json 2>/dev/null || echo "Could not extract fields"Length of output: 213
🏁 Script executed:
#!/bin/bash
# Check if dist/index.js exists and what it contains
if [ -f packages/trpc/dist/index.js ]; then
echo "=== dist/index.js exists, first 20 lines ==="
head -20 packages/trpc/dist/index.js
else
echo "dist/index.js does not exist yet (compilation needed)"
fi
# Also check package.json for main/exports fields
echo ""
echo "=== package.json structure ==="
cat packages/trpc/package.jsonLength of output: 815
🌐 Web query:
tRPC monorepo package structure isomorphic client server
💡 Result:
Short recommendation + example layout for a tRPC monorepo that supports isomorphic client & server.
Key ideas
- Keep apps and reusable code in workspaces: /apps (Next/Expo/etc) and /packages (shared). [1][2]
- Put the tRPC router and server code in a single api package (or packages/api-*) that exports the AppRouter type and any Zod schemas. Client apps import only types from that package — imports are type-only at compile time (common tRPC pattern). [3][8]
- Put client bootstrap (isomorphic client factory / hooks / react-query providers) in a shared package (e.g. packages/trpc-client) so browser and server entrypoints can reuse it. The client package should expose a factory for runtime-specific links/transports (http for browser, server handler or internal link for SSR/Node). [1][5]
- Keep pure types/schemas in a small shared package (packages/types or packages/schemas) if you want stricter separation (optional; T3/turbo patterns often keep an api package as the source of types). [7][12]
Minimal example layout
- /apps
- /web — Next.js / React app (imports client factory)
- /mobile — Expo/React Native (imports client factory)
- /api — server entry (imports router from packages/api or defines server)
- /packages
- /api — tRPC router definitions, procedures, zod schemas, export type AppRouter. (server runtime code lives here or in /apps/api if preferred). [3][8]
- /trpc-client — isomorphic client factory + React hooks/provider wrappers (creates browser HTTP link, Node SSR/Edge handler link, etc.). [1][5]
- /types (optional) — shared TypeScript types and DTOs or zod-only schemas.
Implementation notes
- For SSR/Next: create a server-side transport that uses the same router type; share the client factory to create a client on server and hydrate on client. See Next.js tRPC examples. [3][9]
- Multiple APIs: you can have several api packages (api-a, api-b) and create one client per API in the trpc-client package. [1][2]
- Use pnpm/turborepo/workspaces for fast local linking and consistent builds; many community templates follow apps/packages. [1][4][6]
References / examples
- tRPC examples & community monorepo patterns (apps/packages layout). [3][9]
- Community discussion about sharing tRPC logic across apps (shared hooks, client-per-app pattern). [5]
- Official tRPC repo / patterns explaining AppRouter type and client setup. [8]
- Monorepo example templates using tRPC (react-vite-trpc, create-t3-turbo patterns). [0search0][0reddit12]
If you want, I can:
- produce a concrete file-by-file scaffold (tsconfigs, package.json workspaces, example client factory) for pnpm + Turborepo / Next.js.
Remove executable scripts and fix "main" field in library package.
In a tRPC monorepo, the shared /packages/trpc package should export router definitions and types only—not include executable scripts. According to tRPC patterns, "dev" and "start" scripts belong in app packages (/apps), not shared library packages. Additionally, the "main" field should be "./dist/index.js" to match the "exports" default export, not "index.js".
Changes needed:
- Remove
"dev"and"start"scripts frompackages/trpc/package.json - Update
"main"to"./dist/index.js"
🤖 Prompt for AI Agents
In packages/trpc/package.json around lines 6 to 11, remove the executable-only
scripts ("dev" and "start") from the "scripts" object so this library package
only contains build/test scripts appropriate for a shared package, and update
the "main" field to "./dist/index.js" to match the "exports" default entry;
ensure "build" and "test" remain but delete "dev" and "start", and change the
"main" value to "./dist/index.js".
| .input(z.object({ name: z.string() })) | ||
| .query(({ input }) => { | ||
| return `Hello, ${input.name || 'world'}!`; |
There was a problem hiding this comment.
Clarify input validation logic.
The zod schema requires name to be a string, but the fallback || 'world' on line 12 suggests the intention to handle missing or empty names. Currently, the fallback is unreachable since zod validation will fail before reaching the query function if name is not provided.
Consider one of these approaches:
Option 1: Make name optional with a default value
- .input(z.object({ name: z.string() }))
+ .input(z.object({ name: z.string().optional().default('world') }))
.query(({ input }) => {
- return `Hello, ${input.name || 'world'}!`;
+ return `Hello, ${input.name}!`;
}),Option 2: Handle empty strings explicitly
.input(z.object({ name: z.string() }))
.query(({ input }) => {
- return `Hello, ${input.name || 'world'}!`;
+ return `Hello, ${input.name.trim() || 'world'}!`;
}),📝 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.
| .input(z.object({ name: z.string() })) | |
| .query(({ input }) => { | |
| return `Hello, ${input.name || 'world'}!`; | |
| .input(z.object({ name: z.string().optional().default('world') })) | |
| .query(({ input }) => { | |
| return `Hello, ${input.name}!`; | |
| }), |
| .input(z.object({ name: z.string() })) | |
| .query(({ input }) => { | |
| return `Hello, ${input.name || 'world'}!`; | |
| .input(z.object({ name: z.string() })) | |
| .query(({ input }) => { | |
| return `Hello, ${input.name.trim() || 'world'}!`; | |
| }), |
🤖 Prompt for AI Agents
In packages/trpc/src/index.ts around lines 10 to 12, the zod schema requires
name but the query uses a fallback (|| 'world') which is unreachable; fix by
either making name optional with a default in the schema (e.g.,
z.string().optional().default('world')) so the resolver can rely on input.name,
or keep name required and remove the fallback while handling empty strings
explicitly in the resolver (e.g., if (input.name.trim() === '') use 'world');
update the zod schema and resolver accordingly so validation and fallback
behavior are consistent.
| @@ -0,0 +1 @@ | |||
| {"root":["./src/AppRouter.ts","./src/index.ts"],"version":"5.9.2"} No newline at end of file | |||
There was a problem hiding this comment.
Build artifacts should not be committed to version control.
The tsconfig.tsbuildinfo file is a TypeScript incremental build artifact that is auto-generated during compilation. These files should be added to .gitignore to avoid cluttering the repository.
Add this pattern to your .gitignore:
+*.tsbuildinfoCommittable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In packages/trpc/tsconfig.tsbuildinfo around line 1, a TypeScript incremental
build artifact has been committed; update the repository by adding the
tsconfig.tsbuildinfo pattern to .gitignore (e.g., add a rule to ignore
*.tsbuildinfo or tsconfig.tsbuildinfo), remove the committed file from the index
(git rm --cached packages/trpc/tsconfig.tsbuildinfo) and commit the .gitignore
change and the removal so the artifact is no longer tracked going forward.
|
trpc done |
Summary
What changed
How to test
Checklist
Reviewers
Summary by CodeRabbit
Release Notes
New Features
Dependencies