chore: prepare release v0.3.0#31
Conversation
Add end-to-end Clerk authentication to apps/web, wire it into the Convex backend via ConvexProviderWithClerk, and gate the add mutation behind identity verification. apps/web — authentication integration - package.json: add @clerk/nextjs ^7.5.9 - app/layout.tsx: wrap root layout with ClerkProvider - app/page.tsx: guard UI with Authenticated/Unauthenticated components; show UserButton when signed in and SignInButton when signed out - components/theme-provider.tsx: replace ConvexProvider with ConvexProviderWithClerk (useAuth from @clerk/nextjs) so Convex receives the active Clerk session token; remove dead ThemeHotkey, isTypingTarget, NextThemesProvider, and useTheme code - proxy.ts (Next.js 16 middleware): add clerkMiddleware() protecting all non-public routes; public routes: /sign-in(.*), /sign-up(.*) - app/(auth)/layout.tsx: centered layout for auth pages - app/(auth)/sign-in/[[...sign-in]]/page.tsx: Clerk hosted SignIn component - app/(auth)/sign-up/[[...sign-up]]/page.tsx: Clerk hosted SignUp component apps/widget — cleanup - components/theme-provider.tsx: remove dead ThemeHotkey, isTypingTarget, NextThemesProvider, and useTheme code; simplify to bare ConvexProvider packages/backend — auth configuration and hardening - convex/auth.config.ts: Clerk JWT provider config using CLERK_JWT_ISSUER_DOMAIN; node types via triple-slash reference since this file runs in Node.js (Convex CLI), not the V8 function runtime - convex/users.ts: gate add mutation behind ctx.auth.getUserIdentity() — throws "Not Authenticated" for unauthenticated callers - package.json: add @types/node ^20.19.41 (needed for auth.config.ts) - tsconfig.json: remove deprecated baseUrl (not required for paths in TS 5+) CI/tooling - ci.yml, release.yml, codeql.yml: add NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY to build env (read from GitHub repo variable NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY) so ClerkProvider initialises without throwing during next build in CI Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
feat(web): integrate Clerk authentication with Convex
Update CHANGELOG.md and README.md for the v0.3.0 release. CHANGELOG: - Add [0.3.0] section covering Clerk auth integration, Convex+Clerk session bridging, backend auth hardening, tsconfig baseUrl fix, dead code cleanup in theme-provider, and CI env var additions. Include technical decisions section explaining key choices. - Update compare links to v0.3.0. README: - Add Clerk badge to header - Add Authentication to Features list - Add Clerk row to Tech Stack table - Expand Environment Variables section with all required keys (NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY, CLERK_SECRET_KEY, CLERK_JWT_ISSUER_DOMAIN) with instructions and Clerk Dashboard link Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughClerk authentication is integrated into ChangesClerk Authentication Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Warning There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure. 🔧 ESLint
packages/backend/convex/auth.config.tsParsing error: The keyword 'export' is reserved packages/backend/convex/users.tsParsing error: The keyword 'import' is reserved 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 (1)
apps/web/app/page.tsx (1)
13-25: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winMove the Convex hooks into the authenticated subtree.
useQuery(api.users.getMany)anduseMutation(api.users.add)execute before<Authenticated>decides what to render. If/is public for signed-out users, guests will still initialize the query on Line 14 even though the unauthenticated branch never uses that data.Refactor sketch
export default function Page() { - const users = useQuery(api.users.getMany) - const addUser = useMutation(api.users.add) return ( <> - <Authenticated> - <div className="flex min-h-svh flex-col items-center justify-center"> - <p> apps/web</p> - <UserButton /> - <Button onClick={() => addUser()}>Add</Button> - <div className="mx-auto w-full max-w-sm"> - {JSON.stringify(users, null, 2)} - </div> - </div> - </Authenticated> + <Authenticated> + <AuthenticatedHome /> + </Authenticated> <Unauthenticated> <p>Must be signed In</p> <SignInButton>Sign In!</SignInButton> </Unauthenticated> </> ) }function AuthenticatedHome() { const users = useQuery(api.users.getMany) const addUser = useMutation(api.users.add) return ( <div className="flex min-h-svh flex-col items-center justify-center"> <p> apps/web</p> <UserButton /> <Button onClick={() => addUser()}>Add</Button> <div className="mx-auto w-full max-w-sm"> {JSON.stringify(users, null, 2)} </div> </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 `@apps/web/app/page.tsx` around lines 13 - 25, Move the Convex hook calls out of Page and into the authenticated-only subtree so signed-out users do not initialize them. Create or use a dedicated authenticated component rendered inside <Authenticated> (for example an AuthenticatedHome helper) and place useQuery(api.users.getMany) and useMutation(api.users.add) there, then keep Page as a thin wrapper that only decides between authenticated and unauthenticated UI.
🤖 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 `@apps/web/proxy.ts`:
- Around line 3-8: The middleware in clerkMiddleware is still protecting the
root route, which prevents the new unauthenticated landing page in app/page.tsx
from ever rendering. Update the isPublicRoute matcher in proxy.ts to include "/"
so auth.protect() is skipped for the homepage, while keeping the existing
sign-in and sign-up public routes unchanged.
In `@apps/widget/components/theme-provider.tsx`:
- Around line 5-8: The widget ThemeProvider still creates ConvexReactClient with
an empty-string fallback, so it can boot with a broken client when
NEXT_PUBLIC_CONVEX_URL is missing. Update the top-level Convex client setup in
ThemeProvider to use the same fail-fast guard as the web ThemeProvider: validate
the env var before constructing ConvexReactClient and throw a clear error if it
is absent. Keep the fix localized around the ConvexReactClient initialization
and ConvexProvider usage.
In `@README.md`:
- Around line 147-153: Clarify the setup note in README around the
`CLERK_JWT_ISSUER_DOMAIN` entry so it is clear that only `CONVEX_DEPLOYMENT` is
auto-generated by `convex dev`, while `CLERK_JWT_ISSUER_DOMAIN` must be manually
configured from the Clerk Dashboard. Update the text near the `.env.local` table
to avoid implying both values are generated automatically, and keep the wording
aligned with the `CONVEX_DEPLOYMENT` and `CLERK_JWT_ISSUER_DOMAIN` variable
descriptions.
---
Nitpick comments:
In `@apps/web/app/page.tsx`:
- Around line 13-25: Move the Convex hook calls out of Page and into the
authenticated-only subtree so signed-out users do not initialize them. Create or
use a dedicated authenticated component rendered inside <Authenticated> (for
example an AuthenticatedHome helper) and place useQuery(api.users.getMany) and
useMutation(api.users.add) there, then keep Page as a thin wrapper that only
decides between authenticated and unauthenticated UI.
🪄 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: 3265ab40-b7b7-457d-ba0d-8bb4d80ae016
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (18)
.github/workflows/ci.yml.github/workflows/codeql.yml.github/workflows/release.ymlCHANGELOG.mdREADME.mdapps/web/app/(auth)/layout.tsxapps/web/app/(auth)/sign-in/[[...sign-in]]/page.tsxapps/web/app/(auth)/sign-up/[[...sign-up]]/page.tsxapps/web/app/layout.tsxapps/web/app/page.tsxapps/web/components/theme-provider.tsxapps/web/package.jsonapps/web/proxy.tsapps/widget/components/theme-provider.tsxpackages/backend/convex/auth.config.tspackages/backend/convex/users.tspackages/backend/package.jsonpackages/backend/tsconfig.json
💤 Files with no reviewable changes (1)
- packages/backend/tsconfig.json
| const isPublicRoute = createRouteMatcher(["/sign-in(.*)", "/sign-up(.*)"]) | ||
|
|
||
| export default clerkMiddleware(async (auth, req) => { | ||
| if (!isPublicRoute(req)) { | ||
| await auth.protect() | ||
| } |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
Make / public or the guest homepage can never render.
apps/web/app/page.tsx now has an <Unauthenticated> branch with a sign-in CTA, but this middleware still protects /. Unauthenticated visitors will be redirected by Line 7 before that branch can render, so the new signed-out landing experience is unreachable.
Suggested fix
-const isPublicRoute = createRouteMatcher(["/sign-in(.*)", "/sign-up(.*)"])
+const isPublicRoute = createRouteMatcher([
+ "/",
+ "/sign-in(.*)",
+ "/sign-up(.*)",
+])📝 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.
| const isPublicRoute = createRouteMatcher(["/sign-in(.*)", "/sign-up(.*)"]) | |
| export default clerkMiddleware(async (auth, req) => { | |
| if (!isPublicRoute(req)) { | |
| await auth.protect() | |
| } | |
| const isPublicRoute = createRouteMatcher([ | |
| "/", | |
| "/sign-in(.*)", | |
| "/sign-up(.*)", | |
| ]) | |
| export default clerkMiddleware(async (auth, req) => { | |
| if (!isPublicRoute(req)) { | |
| await auth.protect() | |
| } |
🤖 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 `@apps/web/proxy.ts` around lines 3 - 8, The middleware in clerkMiddleware is
still protecting the root route, which prevents the new unauthenticated landing
page in app/page.tsx from ever rendering. Update the isPublicRoute matcher in
proxy.ts to include "/" so auth.protect() is skipped for the homepage, while
keeping the existing sign-in and sign-up public routes unchanged.
| const convex = new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL || "") | ||
|
|
||
| function ThemeProvider({ | ||
| children, | ||
| ...props | ||
| }: React.ComponentProps<typeof NextThemesProvider>) { | ||
| function ThemeProvider({ children }: { children: React.ReactNode }) { | ||
| return <ConvexProvider client={convex}>{children}</ConvexProvider> |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify there are no remaining empty-string Convex URL fallbacks.
rg -n 'new ConvexReactClient\(process\.env\.NEXT_PUBLIC_CONVEX_URL \|\| ""\)|NEXT_PUBLIC_CONVEX_URL \|\| ""' .Repository: RISHII7/echo
Length of output: 275
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== files ==\n'
git ls-files 'apps/**/theme-provider.tsx' 'apps/**/theme-provider.*' 'apps/**/convex*' 'apps/**/provider*' | sed 's#^`#-` #'
printf '\n== widget theme provider ==\n'
sed -n '1,120p' apps/widget/components/theme-provider.tsx
printf '\n== search for NEXT_PUBLIC_CONVEX_URL guards/usages ==\n'
rg -n 'NEXT_PUBLIC_CONVEX_URL|ConvexReactClient|ConvexProvider' apps -g '!**/node_modules/**'Repository: RISHII7/echo
Length of output: 1685
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== file sizes ==\n'
for f in apps/widget/components/theme-provider.tsx; do
wc -l "$f"
done
printf '\n== nearby provider file(s) ==\n'
fd -a 'theme-provider.tsx' apps
printf '\n== web provider candidates ==\n'
rg -n 'throw new Error\(.*NEXT_PUBLIC_CONVEX_URL|NEXT_PUBLIC_CONVEX_URL' apps/web apps -g '!**/node_modules/**'Repository: RISHII7/echo
Length of output: 1137
🌐 Web query:
ConvexReactClient constructor empty URL NEXT_PUBLIC_CONVEX_URL behavior
💡 Result:
The ConvexReactClient constructor requires a valid URL string as its first argument to connect to a Convex deployment [1][2]. If you pass an empty string (or a falsy/invalid value) to the constructor, the client will attempt to initialize with that value, which will typically result in a connection failure or runtime error when the client attempts to communicate with the Convex backend over WebSocket [3][4]. Regarding NEXT_PUBLIC_CONVEX_URL: 1. Automatic Defaulting: The ConvexReactClient does not automatically "look up" or default to process.env.NEXT_PUBLIC_CONVEX_URL if you pass an empty string to the constructor [1][2]. You must explicitly pass the environment variable to the constructor yourself [5][6]. 2. Best Practice: It is standard practice in Next.js applications to pass the environment variable directly, often with a fallback or ensuring it is defined, for example: new ConvexReactClient(process.env.NEXT_PUBLIC_CONVEX_URL!) [7]. 3. Explicit Undefined Behavior: If you are using specific methods or helper functions that accept optional URL parameters, note that recent updates have changed behavior to throw an explicit error if you pass undefined where a URL is expected, rather than silently falling back to a default [8]. In summary, always ensure that the string passed to the ConvexReactClient constructor is a valid, non-empty URL string for your deployment [1][2]. If your environment variable is missing, the application will likely fail at runtime when the client tries to initialize [7].
Citations:
- 1: https://docs.convex.dev/api/classes/react.ConvexReactClient.md
- 2: https://docs.convex.dev/api/classes/react.ConvexReactClient
- 3: https://docs.convex.dev/client/react
- 4: https://github.com/get-convex/convex-js/blob/main/src/react/client.ts
- 5: https://docs.convex.dev/client/react/deployment-urls.md
- 6: https://docs.convex.dev/client/react/deployment-urls
- 7: https://docs.convex.dev/client/nextjs/app-router/
- 8: Fix Next.js SDK URL defaulting to throw on explicit undefined get-convex/convex-backend#403
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== search for the exact guard message ==\n'
rg -n 'Missing NEXT_PUBLIC_CONVEX_URL|NEXT_PUBLIC_CONVEX_URL in your \\.env file|Convex URL' apps .
printf '\n== theme-provider files ==\n'
fd -a 'theme-provider.tsx' appsRepository: RISHII7/echo
Length of output: 563
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n== search for other Convex client initializations ==\n'
rg -n 'new ConvexReactClient\(' apps packages src . -g '!**/node_modules/**' || true
printf '\n== search for env guards in widget/web apps ==\n'
rg -n 'process\.env\.NEXT_PUBLIC_CONVEX_URL|throw new Error' apps/widget apps/web -g '!**/node_modules/**' || trueRepository: RISHII7/echo
Length of output: 1209
Fail fast when NEXT_PUBLIC_CONVEX_URL is missing. apps/widget/components/theme-provider.tsx still falls back to "" when constructing ConvexReactClient, while apps/web/components/theme-provider.tsx throws on a missing value. Use the same guard here so misconfigured widget builds don't start with a broken Convex client.
🤖 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 `@apps/widget/components/theme-provider.tsx` around lines 5 - 8, The widget
ThemeProvider still creates ConvexReactClient with an empty-string fallback, so
it can boot with a broken client when NEXT_PUBLIC_CONVEX_URL is missing. Update
the top-level Convex client setup in ThemeProvider to use the same fail-fast
guard as the web ThemeProvider: validate the env var before constructing
ConvexReactClient and throw a clear error if it is absent. Keep the fix
localized around the ConvexReactClient initialization and ConvexProvider usage.
| Create `packages/backend/.env.local` (auto-generated by `convex dev`): | ||
|
|
||
| | Variable | Description | Required | | ||
| | ------------------------- | ------------------------------------ | -------- | | ||
| | `CONVEX_DEPLOYMENT` | Convex deployment identifier | Yes | | ||
| | `CLERK_JWT_ISSUER_DOMAIN` | Clerk JWT issuer URL for Convex auth | Yes | | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Clarify the CLERK_JWT_ISSUER_DOMAIN setup note.
The table states that packages/backend/.env.local is "auto-generated by convex dev", but CLERK_JWT_ISSUER_DOMAIN is not auto-generated — it must be manually configured from the Clerk Dashboard. Only CONVEX_DEPLOYMENT is automatically created by convex dev. This could mislead developers setting up the backend.
📝 Suggested clarification
-Create `packages/backend/.env.local` (auto-generated by `convex dev`):
+Create `packages/backend/.env.local` (`CONVEX_DEPLOYMENT` is auto-generated by `convex dev`):📝 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.
| Create `packages/backend/.env.local` (auto-generated by `convex dev`): | |
| | Variable | Description | Required | | |
| | ------------------------- | ------------------------------------ | -------- | | |
| | `CONVEX_DEPLOYMENT` | Convex deployment identifier | Yes | | |
| | `CLERK_JWT_ISSUER_DOMAIN` | Clerk JWT issuer URL for Convex auth | Yes | | |
| Create `packages/backend/.env.local` (`CONVEX_DEPLOYMENT` is auto-generated by `convex dev`): | |
| | Variable | Description | Required | | |
| | ------------------------- | ------------------------------------ | -------- | | |
| | `CONVEX_DEPLOYMENT` | Convex deployment identifier | Yes | | |
| | `CLERK_JWT_ISSUER_DOMAIN` | Clerk JWT issuer URL for Convex auth | Yes | |
🤖 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 `@README.md` around lines 147 - 153, Clarify the setup note in README around
the `CLERK_JWT_ISSUER_DOMAIN` entry so it is clear that only `CONVEX_DEPLOYMENT`
is auto-generated by `convex dev`, while `CLERK_JWT_ISSUER_DOMAIN` must be
manually configured from the Clerk Dashboard. Update the text near the
`.env.local` table to avoid implying both values are generated automatically,
and keep the wording aligned with the `CONVEX_DEPLOYMENT` and
`CLERK_JWT_ISSUER_DOMAIN` variable descriptions.
Release v0.3.0 — Clerk Authentication
This PR merges
release/0.3.0intomainto ship full Clerk authentication integrated with the Convex real-time backend.What's in this release
New: Clerk authentication (
apps/web)ClerkProviderwraps the root layoutproxy.tsmiddleware (Next.js 16) protects all routes; sign-in and sign-up are publicapp/(auth)/sign-inandapp/(auth)/sign-uppages with Clerk hosted UIAuthenticated/Unauthenticatedguards on the home page withUserButton+SignInButtonConvexProviderWithClerkbridges Clerk session tokens into Convex callsBackend auth hardening (
packages/backend)convex/auth.config.ts— Clerk JWT provider configconvex/users.ts—addmutation requires authenticated identitybaseUrldeprecation warning fixed intsconfig.jsonCleanup
ThemeHotkey/isTypingTarget/NextThemesProvidercode removed from both appsCI
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEYadded to build env from GitHub repo variableDocs
Checklist
feature/clerk-authpnpm lint— clean locallypnpm typecheck— clean locallypnpm build— 4 routes built including sign-in/sign-up and proxy middlewarev0.3.0tag to be created after merge🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Documentation