Skip to content

Conversation

philip
Copy link

@philip philip commented Aug 27, 2025

Please review, nitpicking is encouraged :)

Summary by CodeRabbit

  • New Features
    • Added a React “Neon Basic” starter example with Stack/Neon Auth, TanStack Router/Start, client-only auth handler, posts list/detail via server functions, error and 404 boundaries, SEO helpers, and Tailwind-based styling.
    • Includes Vite setup with Neon Postgres plugin, router factory, utilities, and reusable components.
  • Documentation
    • New README, authentication and build troubleshooting guides, and an environment template to configure required keys.
  • Chores
    • Added project tooling and configs: package scripts, Vite/Tailwind/PostCSS settings, TypeScript config, editor settings, and ignore files.

Copy link
Contributor

coderabbitai bot commented Aug 27, 2025

Walkthrough

Adds a complete TanStack Start + Neon Auth example under examples/react/start-neon-basic, including configs, documentation, Tailwind setup, Vite/TS integration, auth wiring, server functions, routes (root, handler, authed posts), utilities, components, and project scaffolding.

Changes

Cohort / File(s) Summary of changes
Repo ignores & editor
examples/react/start-neon-basic/.gitignore, .../.prettierignore, .../.vscode/settings.json
New ignore patterns for builds, caches, lockfiles; Prettier/VSCode exclude and readonly settings for generated routeTree.gen.ts.
Documentation
.../README.md, .../docs/*, .../env.example
Added README, authentication and build troubleshooting guides, and example env file with required Stack/Neon variables.
Build & tooling config
.../package.json, .../tsconfig.json, .../vite.config.ts, .../postcss.config.mjs, .../tailwind.config.mjs
New project scripts and deps; TS strict config and paths; Vite with TanStack Start and Neon Postgres plugin; PostCSS with Tailwind/Autoprefixer; Tailwind content paths.
Styles
.../src/styles/app.css
Tailwind directives and base light/dark theme styles.
Router setup
.../src/router.tsx, .../src/routes/__root.tsx
Router factory with routeTree; root route with head/meta, error/notFound boundaries, app shell, providers, and navigation.
Auth handler & client
.../src/routes/handler.$.tsx, .../src/stack.ts
Client-only StackHandler route; StackClientApp setup with env validation and TanStack navigation adapter.
Components
.../src/components/DefaultCatchBoundary.tsx, .../src/components/NotFound.tsx
Error boundary with retry/back navigation; NotFound component with back/start-over actions.
Hook
.../src/hooks/useMutation.ts
New useMutation hook managing async mutation state and callbacks.
Routes: posts
.../src/routes/_authed/posts.tsx, .../src/routes/_authed/posts.$postId.tsx, .../src/routes/_authed/posts.index.tsx, .../src/routes/index.tsx
Authed posts list with nested routing, per-post loader/error/notFound, index prompt, and simple home route.
Utilities
.../src/utils/neon.ts, .../src/utils/posts.ts, .../src/utils/seo.ts
Neon Pool client factory; server functions to fetch posts/post with notFound handling; SEO tag builder utility.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant U as User
  participant R as TanStack Router
  participant H as /handler/$ (Client)
  participant S as StackClientApp
  participant A as Stack Auth Service

  U->>R: Navigate to /handler/sign-in
  R->>H: Render HandlerComponent (client-only)
  H->>S: <StackHandler app=S location=pathname>
  S->>A: Start auth flow (redirect/PKCE)
  A-->>U: Provider UI / redirect
  U-->>A: Complete auth
  A-->>S: Callback with tokens
  S-->>H: Session established
  H-->>R: Redirect to app route
  R-->>U: Authenticated view
Loading
sequenceDiagram
  autonumber
  participant U as User
  participant R as Router
  participant P as /_authed/posts
  participant S as ServerFn fetchPosts
  participant N as Network (jsonplaceholder)

  U->>R: Visit /_authed/posts
  R->>P: Load route (loader)
  P->>S: fetchPosts()
  S->>N: GET /posts
  N-->>S: 200 JSON (list)
  S-->>P: First 10 posts
  P-->>U: Render list and <Outlet>

  U->>R: Select a post (/posts/$postId)
  R->>P: Load child route loader
  P->>S: fetchPost(postId)
  S->>N: GET /posts/{id}
  alt 200 OK
    N-->>S: Post
    S-->>P: Post data
    P-->>U: Render PostComponent
  else 404
    N-->>S: 404
    S-->>P: notFound()
    P-->>U: NotFound component
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I hop through routes with whiskers bright,
New posts to fetch by day and night.
Auth doors swing—sighed “who goes there?”—
I flash my keys of Neon air.
Tailwind breezes, Vite ignites—
Ship the start, my carrot bytes! 🥕✨

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
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: 17

🧹 Nitpick comments (39)
examples/react/start-neon-basic/.prettierignore (1)

1-2: Tighten directory patterns to avoid unintended file matches

Use trailing slashes so files named “build”/“public” aren’t ignored accidentally.

-**/build
-**/public
+**/build/
+**/public/
examples/react/start-neon-basic/.gitignore (3)

14-16: Split inline comment for readability and add explicit pattern

This keeps the pattern clear and prepares room for more generated-file ignores.

-/public/build# Sentry Config File
-.env.sentry-build-plugin
+/public/build
+# Sentry Config File
+.env.sentry-build-plugin

7-7: Ignore all local env variants while keeping env.example tracked

Prevents accidental commits of environment files across modes.

 .env
+.env.*
+!.env.example

20-21: Also ignore the generated route tree for consistency with Prettier/VSCode

Avoids accidental commits; aligns with .prettierignore and editor settings.

 .tanstack
+routeTree.gen.ts
examples/react/start-neon-basic/tailwind.config.mjs (1)

1-4: Consider scanning root HTML templates too (harmless if absent)

Covers common Vite/Start entry HTML so Tailwind picks up classes there as well.

 export default {
-  content: ['./src/**/*.{js,jsx,ts,tsx}'],
+  content: ['./index.html', './app.html', './src/**/*.{js,jsx,ts,tsx}'],
 }
examples/react/start-neon-basic/package.json (1)

1-14: Harden package metadata for an example app.

Mark private and declare supported Node to reduce accidental publish and clarify runtime.

 {
   "name": "start-neon-basic",
   "version": "1.0.0",
   "description": "",
-  "main": "index.js",
+  "private": true,
+  "engines": { "node": ">=18.17.0" },
+  "main": "index.js",
examples/react/start-neon-basic/env.example (2)

13-14: Tighten guidance and remove trailing space.

Add a placeholder for the claim URL mentioned in the README and trim whitespace.

-# Optional: Set DATABASE_URL or leave commented out to auto-create with Neon Launchpad
-# DATABASE_URL=postgresql://username:password@host:port/database 
+# Optional: Set DATABASE_URL or leave commented out to auto-create with Neon Launchpad
+# DATABASE_URL=postgresql://username:password@host:port/database
+# NEON_CLAIM_URL=auto-populated-by-launchpad (do not commit)

1-12: Call out secret handling explicitly.

Add a “do not commit .env” reminder to reduce accidental leaks.

 # Stack Auth (Neon Auth) Configuration
-# Copy this file to .env and fill in your actual values
+# Copy this file to .env (or .env.local) and fill in your actual values.
+# Never commit files containing real secrets.
examples/react/start-neon-basic/src/components/NotFound.tsx (1)

10-15: Prevent accidental form submission by setting type="button".

Small a11y/behavior improvement if this lives inside a form.

-        <button
+        <button
+          type="button"
           onClick={() => window.history.back()}
examples/react/start-neon-basic/README.md (6)

69-79: Add a language to the fenced code block (markdownlint MD040).

-```
+```text
 src/
 ├── routes/
 │   ├── __root.tsx          # Root with StackProvider
 │   ├── handler.$.tsx       # Auth callbacks (client-only)
 │   ├── index.tsx           # Home page
 │   └── _authed/            # Protected routes
 ├── stack.ts                # Stack Auth configuration
 └── utils/                  # Database utilities

---

`15-22`: **Unify package manager usage.**

“Quickest” uses npm, “Quick Start” uses pnpm. Pick one (or show both) to avoid confusion.


```diff
-```bash
-npx gitpick TanStack/router/tree/main/examples/react/start-neon-basic start-neon-basic
-cd start-neon-basic
-npm install
-npm run dev
-```
+```bash
+npx gitpick TanStack/router/tree/main/examples/react/start-neon-basic start-neon-basic
+cd start-neon-basic
+pnpm install
+pnpm dev
+```

54-60: Clarify the auto-creation language and when to set DATABASE_URL.

Minor grammar + add explicit override timing.

-This example uses the `@neondatabase/vite-plugin-postgres` plugin which automatically:
+This example uses the `@neondatabase/vite-plugin-postgres` plugin, which automatically:
@@
-You can override this by setting your own `DATABASE_URL` in the `.env` file before running `pnpm dev`.
+You can override this by setting your own `DATABASE_URL` in `.env` before running `pnpm dev`.

32-42: Document the Launchpad claim URL variable to match env.example.

-   - Claim your project when prompted (a browser tab will open automatically, and the claim URL is also saved to .env)
+   - Claim your project when prompted (a browser tab will open automatically, and the claim URL is also saved to `.env` as `NEON_CLAIM_URL`)

3-14: Tiny copyedits for grammar/clarity.

Hyphenation and articles.

-SSR-compatible authentication with Neon Auth and TanStack Start.
+SSR-compatible authentication with Neon Auth and TanStack Start.
@@
-- **Auto Database Setup** - Neon Launchpad creates database connection
+- **Auto Database Setup** - Neon Launchpad creates a database connection

82-86: Add one more common fix.

When routes change, dev servers often need a restart.

 - **Route conflicts**: Delete `src/routeTree.gen.ts` and restart
+ - **Route conflicts**: Delete `src/routeTree.gen.ts` and restart the dev server
examples/react/start-neon-basic/tsconfig.json (1)

1-22: Add Vite types, drop allowJs (stricter), and prefer verbatimModuleSyntax.

These tweaks improve DX (import.meta types), avoid accidental JS creep in a strict TS example, and encourage clean ESM.

 {
   "include": ["**/*.ts", "**/*.tsx"],
   "compilerOptions": {
     "strict": true,
     "esModuleInterop": true,
     "jsx": "react-jsx",
     "module": "ESNext",
     "moduleResolution": "Bundler",
     "lib": ["DOM", "DOM.Iterable", "ES2022"],
     "isolatedModules": true,
     "resolveJsonModule": true,
     "skipLibCheck": true,
     "target": "ES2022",
-    "allowJs": true,
+    "allowJs": false,
     "forceConsistentCasingInFileNames": true,
     "baseUrl": ".",
+    "types": ["vite/client"],
+    "verbatimModuleSyntax": true,
     "paths": {
       "~/*": ["./src/*"]
     },
     "noEmit": true
   }
 }
examples/react/start-neon-basic/vite.config.ts (1)

6-19: LGTM; consider excluding @stackframe/react from dev pre-bundling.

Some stacks need both ssr.noExternal and optimizeDeps.exclude to avoid Vite pre-bundle hiccups in dev.

 export default defineConfig({
   ssr: {
     noExternal: [/^@stackframe\/.*/],
   },
+  optimizeDeps: {
+    exclude: ['@stackframe/react'],
+  },
   server: {
     port: 3000,
   },
   plugins: [
     postgresPlugin(),
     tsConfigPaths({
       projects: ['./tsconfig.json'],
     }),
     tanstackStart(),
   ],
 })
examples/react/start-neon-basic/src/stack.ts (1)

15-33: Clarify env error: list only missing client vars; mention server secret separately.

Currently the message always lists STACK_SECRET_SERVER_KEY, which can mislead when only client vars are missing.

- if (!projectId || !publishableClientKey) {
-   throw new Error(`
+if (!projectId || !publishableClientKey) {
+  const missing = [
+    !projectId && 'VITE_STACK_PROJECT_ID',
+    !publishableClientKey && 'VITE_STACK_PUBLISHABLE_CLIENT_KEY',
+  ].filter(Boolean).join(', ')
+  throw new Error(`
 🚨 Stack Auth Configuration Error
 
-Missing required environment variables:
-- VITE_STACK_PROJECT_ID
-- VITE_STACK_PUBLISHABLE_CLIENT_KEY
-- STACK_SECRET_SERVER_KEY
+Missing required client environment variables:
+- ${missing}
+
+Additionally required for server-only code:
+- STACK_SECRET_SERVER_KEY
 
 To fix this:
 1. Run 'pnpm dev' to trigger Neon Launchpad (browser tab will open)
 2. Claim your project in the browser tab, or use the claim URL saved in .env
 3. Navigate to "Auth" section -> "Enable Neon Auth" -> "Configuration" -> "React"
 4. Copy your credentials to your .env file
 
 Note: This example uses Neon Auth (which is built on Stack Auth).
 Do not go to https://app.stack-auth.com - use https://neon.com instead.
   `)
 }
examples/react/start-neon-basic/src/utils/seo.ts (1)

12-33: Avoid undefined meta contents and use property for Open Graph tags.

Prevents emitting tags like content: undefined and aligns OG with spec. Keeps Twitter tags unchanged.

 export const seo = ({
   title,
   description,
   keywords,
   image,
 }: {
   title: string
   description?: string
   image?: string
   keywords?: string
 }) => {
-  const tags = [
-    { title },
-    { name: 'description', content: description },
-    { name: 'keywords', content: keywords },
-    { name: 'twitter:title', content: title },
-    { name: 'twitter:description', content: description },
-    { name: 'twitter:creator', content: '@tannerlinsley' },
-    { name: 'twitter:site', content: '@tannerlinsley' },
-    { name: 'og:type', content: 'website' },
-    { name: 'og:title', content: title },
-    { name: 'og:description', content: description },
-    ...(image
-      ? [
-          { name: 'twitter:image', content: image },
-          { name: 'twitter:card', content: 'summary_large_image' },
-          { name: 'og:image', content: image },
-        ]
-      : []),
-  ]
+  const tags = [
+    { title },
+    ...(description ? [{ name: 'description', content: description }] : []),
+    ...(keywords ? [{ name: 'keywords', content: keywords }] : []),
+    { name: 'twitter:title', content: title },
+    ...(description ? [{ name: 'twitter:description', content: description }] : []),
+    { name: 'twitter:creator', content: '@tannerlinsley' },
+    { name: 'twitter:site', content: '@tannerlinsley' },
+    { property: 'og:type', content: 'website' },
+    { property: 'og:title', content: title },
+    ...(description ? [{ property: 'og:description', content: description }] : []),
+    ...(image
+      ? [
+          { name: 'twitter:image', content: image },
+          { name: 'twitter:card', content: 'summary_large_image' },
+          { property: 'og:image', content: image },
+        ]
+      : []),
+  ] as const

Minor: consider making the Twitter handles configurable via parameters with sensible defaults.

examples/react/start-neon-basic/src/hooks/useMutation.ts (2)

15-34: Include onSuccess in dependencies to avoid stale closures.

mutate closes over opts.onSuccess but the dependency array only lists opts.fn. Add opts.onSuccess to keep behavior predictable.

-    [opts.fn],
+    [opts.fn, opts.onSuccess],

15-34: API ergonomics: decide whether mutate should reject on error.

Currently mutate resolves to undefined on error. Consider rethrowing to enable try/catch at call sites, or provide both mutate (swallow) and mutateAsync (reject) variants for clarity.

examples/react/start-neon-basic/docs/BUILD_TROUBLESHOOTING.md (2)

20-23: Specify language for the fenced code block.

Adds language to satisfy markdownlint MD040 and improve rendering.

-```
+```text
 Error [ERR_MODULE_NOT_FOUND]: Cannot find module '@tanstack/react-start/dist/esm/plugin-vite.js'

---

`111-121`: **Document “as any” hack as a last resort and gate behind an env flag.**

Encourage a safer workflow: prefer aligning Node/pnpm/lockfile; if a bypass is documented, flag it as temporary and conditionally applied.


I can propose a short patch showing a build-time env guard around type assertions in the affected packages if you want it in this PR or a follow-up.

</blockquote></details>
<details>
<summary>examples/react/start-neon-basic/src/routes/_authed/posts.index.tsx (1)</summary><blockquote>

`1-4`: **Add a blank line after imports.**

Matches common style rules (import/newline-after-import).


```diff
 import { createFileRoute } from '@tanstack/react-router'
+
 export const Route = createFileRoute('/_authed/posts/')({
   component: PostsIndexComponent,
 })
examples/react/start-neon-basic/src/routes/index.tsx (1)

1-4: Add a blank line after imports.

Consistent with the rest of the codebase and ESLint rule import/newline-after-import.

 import { createFileRoute } from '@tanstack/react-router'
+
 export const Route = createFileRoute('/')({
   component: Home,
 })
examples/react/start-neon-basic/src/styles/app.css (2)

10-12: Global border color may have unintended side-effects.
This affects all elements (hr, tables, inputs, etc.). If the intent is a default for bordered elements only, consider scoping to specific components or relying on Tailwind classes at call sites.


19-21: Don’t globally nuke outlines; risks a11y regressions.
Safer: only hide outlines when focus isn’t keyboard-driven, and explicitly style focus-visible.

Apply this diff:

-  .using-mouse * {
-    outline: none !important;
-  }
+  /* Hide focus outline on mouse focus only; keep keyboard focus-visible */
+  .using-mouse :focus {
+    outline: none;
+  }
+  :focus-visible {
+    outline: 2px solid #3b82f6;
+    outline-offset: 2px;
+  }
examples/react/start-neon-basic/src/router.tsx (1)

5-12: Optional: add explicit return type for clarity.
Keeps the augmentation and callers aligned if the factory shape changes.

-export function createRouter() {
+export function createRouter(): ReturnType<typeof createTanStackRouter> {
   const router = createTanStackRouter({
     routeTree,
     scrollRestoration: true,
   })
   return router
 }
examples/react/start-neon-basic/src/routes/handler.$.tsx (1)

22-23: ts-ignore likely unnecessary.
createFileRoute accepts the path literal. Drop the directive unless there’s a concrete type error.

-// @ts-ignore - TanStack Start file-based routing expects no arguments
 export const Route = createFileRoute("/handler/$")({
examples/react/start-neon-basic/src/components/DefaultCatchBoundary.tsx (2)

24-31: Use type="button" to avoid implicit submit in forms.
Small safeguard.

-        <button
+        <button
+          type="button"
           onClick={() => {
             router.invalidate()
           }}

40-46: Graceful fallback if there’s no back history.
Navigate home when history is empty.

-            onClick={(e) => {
-              e.preventDefault()
-              window.history.back()
-            }}
+            onClick={(e) => {
+              e.preventDefault()
+              if (window.history.length > 1) {
+                window.history.back()
+              } else {
+                router.navigate({ to: '/' })
+              }
+            }}
examples/react/start-neon-basic/src/routes/_authed/posts.tsx (2)

5-7: Minor: you can pass the loader directly.
Equivalent and a bit cleaner.

-export const Route = createFileRoute('/_authed/posts')({
-  loader: () => fetchPosts(),
+export const Route = createFileRoute('/_authed/posts')({
+  loader: fetchPosts,
   component: PostsComponent,
 })

25-27: Active state styling could be dark-mode aware.
Optional: add dark variant to keep contrast.

-                  activeProps={{ className: 'text-black font-bold' }}
+                  activeProps={{ className: 'text-black dark:text-white font-bold' }}
examples/react/start-neon-basic/src/routes/__root.tsx (4)

31-36: Invalid manifest link “color” and wrong hex; prefer theme-color meta.

color isn’t a valid attribute for rel="manifest", and #fffff has 5 digits. Use a meta theme-color and drop color from the manifest link.

Apply this diff:

       ...seo({
         title:
           'TanStack Start | Type-Safe, Client-First, Full-Stack React Framework',
         description: `TanStack Start is a type-safe, client-first, full-stack React framework. `,
       }),
+      { name: 'theme-color', content: '#ffffff' },
     ],
     links: [
       { rel: 'stylesheet', href: appCss },
       {
         rel: 'apple-touch-icon',
@@
-      { rel: 'manifest', href: '/site.webmanifest', color: '#fffff' },
+      { rel: 'manifest', href: '/site.webmanifest' },

Also applies to: 56-56


81-81: Add lang attribute to for a11y/SEO.

Small but standard improvement.

Apply this diff:

-    <html>
+    <html lang="en">

130-131: Replace any with an inferred SDK user type.

Improves type-safety with zero maintenance.

Apply this diff:

-  const [user, setUser] = React.useState<any>(null)
+  const [user, setUser] =
+    React.useState<Awaited<ReturnType<typeof stackClientApp.getUser>>>(null)

121-121: Gate devtools to dev builds only.

Prevents shipping devtools to production.

Apply this diff:

-      <TanStackRouterDevtools position="bottom-right" />
+      {import.meta.env.DEV && (
+        <TanStackRouterDevtools position="bottom-right" />
+      )}
examples/react/start-neon-basic/src/routes/_authed/posts.$postId.tsx (2)

7-7: Optionally validate/normalize postId before fetching.

Guards against non-numeric IDs and aligns with the upstream API.

Apply this diff (adds notFound import and validates):

-import { ErrorComponent, createFileRoute } from '@tanstack/react-router'
+import { ErrorComponent, createFileRoute, notFound } from '@tanstack/react-router'
@@
-  loader: ({ params: { postId } }) => fetchPost({ data: postId }),
+  loader: ({ params: { postId } }) => {
+    const id = Number(postId)
+    if (!Number.isFinite(id) || id < 1) {
+      throw notFound()
+    }
+    return fetchPost({ data: String(id) })
+  },

23-26: Minor a11y nit: consider semantic heading level.

If this is the page’s primary heading, use h1/h2 as appropriate for the route hierarchy.

Apply this diff:

-      <h4 className="text-xl font-bold underline">{post.title}</h4>
+      <h2 className="text-xl font-bold underline">{post.title}</h2>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a98c89c and 8218ce3.

📒 Files selected for processing (27)
  • examples/react/start-neon-basic/.gitignore (1 hunks)
  • examples/react/start-neon-basic/.prettierignore (1 hunks)
  • examples/react/start-neon-basic/.vscode/settings.json (1 hunks)
  • examples/react/start-neon-basic/README.md (1 hunks)
  • examples/react/start-neon-basic/docs/AUTHENTICATION_TROUBLESHOOTING.md (1 hunks)
  • examples/react/start-neon-basic/docs/BUILD_TROUBLESHOOTING.md (1 hunks)
  • examples/react/start-neon-basic/env.example (1 hunks)
  • examples/react/start-neon-basic/package.json (1 hunks)
  • examples/react/start-neon-basic/postcss.config.mjs (1 hunks)
  • examples/react/start-neon-basic/src/components/DefaultCatchBoundary.tsx (1 hunks)
  • examples/react/start-neon-basic/src/components/NotFound.tsx (1 hunks)
  • examples/react/start-neon-basic/src/hooks/useMutation.ts (1 hunks)
  • examples/react/start-neon-basic/src/router.tsx (1 hunks)
  • examples/react/start-neon-basic/src/routes/__root.tsx (1 hunks)
  • examples/react/start-neon-basic/src/routes/_authed/posts.$postId.tsx (1 hunks)
  • examples/react/start-neon-basic/src/routes/_authed/posts.index.tsx (1 hunks)
  • examples/react/start-neon-basic/src/routes/_authed/posts.tsx (1 hunks)
  • examples/react/start-neon-basic/src/routes/handler.$.tsx (1 hunks)
  • examples/react/start-neon-basic/src/routes/index.tsx (1 hunks)
  • examples/react/start-neon-basic/src/stack.ts (1 hunks)
  • examples/react/start-neon-basic/src/styles/app.css (1 hunks)
  • examples/react/start-neon-basic/src/utils/neon.ts (1 hunks)
  • examples/react/start-neon-basic/src/utils/posts.ts (1 hunks)
  • examples/react/start-neon-basic/src/utils/seo.ts (1 hunks)
  • examples/react/start-neon-basic/tailwind.config.mjs (1 hunks)
  • examples/react/start-neon-basic/tsconfig.json (1 hunks)
  • examples/react/start-neon-basic/vite.config.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (8)
examples/react/start-neon-basic/src/components/DefaultCatchBoundary.tsx (6)
examples/react/start-clerk-basic/app/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
examples/react/start-basic-react-query/app/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
examples/react/start-basic-rsc/app/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
examples/react/start-supabase-basic/app/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (11-54)
examples/react/start-basic-auth/app/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
examples/react/start-basic-static/app/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (10-53)
examples/react/start-neon-basic/src/routes/handler.$.tsx (2)
examples/react/start-neon-basic/src/stack.ts (1)
  • stackClientApp (35-40)
examples/react/start-neon-basic/src/routes/index.tsx (1)
  • Route (2-4)
examples/react/start-neon-basic/src/routes/_authed/posts.index.tsx (2)
examples/react/start-neon-basic/src/routes/_authed/posts.$postId.tsx (1)
  • Route (6-13)
examples/react/start-neon-basic/src/routes/_authed/posts.tsx (1)
  • Route (4-7)
examples/react/start-neon-basic/src/components/NotFound.tsx (5)
examples/react/start-basic-auth/app/components/NotFound.tsx (1)
  • NotFound (3-25)
examples/react/start-basic-static/app/components/NotFound.tsx (1)
  • NotFound (3-25)
examples/react/start-supabase-basic/app/components/NotFound.tsx (1)
  • NotFound (3-25)
examples/react/start-convex-trellaux/app/components/NotFound.tsx (1)
  • NotFound (3-25)
examples/react/start-clerk-basic/app/components/NotFound.tsx (1)
  • NotFound (3-25)
examples/react/start-neon-basic/src/routes/_authed/posts.tsx (3)
examples/react/start-neon-basic/src/routes/_authed/posts.$postId.tsx (1)
  • Route (6-13)
examples/react/start-neon-basic/src/routes/_authed/posts.index.tsx (1)
  • Route (2-4)
examples/react/start-neon-basic/src/utils/posts.ts (1)
  • fetchPosts (29-37)
examples/react/start-neon-basic/src/routes/index.tsx (2)
examples/react/start-neon-basic/src/routes/__root.tsx (1)
  • Route (21-69)
examples/react/start-neon-basic/src/routes/_authed/posts.index.tsx (1)
  • Route (2-4)
examples/react/start-neon-basic/src/routes/__root.tsx (4)
examples/react/start-neon-basic/src/utils/seo.ts (1)
  • seo (1-33)
examples/react/start-neon-basic/src/components/DefaultCatchBoundary.tsx (1)
  • DefaultCatchBoundary (11-54)
examples/react/start-neon-basic/src/components/NotFound.tsx (1)
  • NotFound (3-25)
examples/react/start-neon-basic/src/stack.ts (1)
  • stackClientApp (35-40)
examples/react/start-neon-basic/src/routes/_authed/posts.$postId.tsx (2)
examples/react/start-neon-basic/src/utils/posts.ts (1)
  • fetchPost (11-27)
examples/react/start-neon-basic/src/components/NotFound.tsx (1)
  • NotFound (3-25)
🪛 ESLint
examples/react/start-neon-basic/src/routes/handler.$.tsx

[error] 2-2: ../stack import should occur after import of react

(import/order)


[error] 3-3: Member 'createFileRoute' of the import declaration should be sorted alphabetically.

(sort-imports)


[error] 4-4: Member 'useEffect' of the import declaration should be sorted alphabetically.

(sort-imports)

examples/react/start-neon-basic/src/routes/_authed/posts.index.tsx

[error] 1-1: Expected 1 empty line after import statement not followed by another import.

(import/newline-after-import)

examples/react/start-neon-basic/src/routes/index.tsx

[error] 1-1: Expected 1 empty line after import statement not followed by another import.

(import/newline-after-import)

🪛 LanguageTool
examples/react/start-neon-basic/README.md

[grammar] ~5-~5: There might be a mistake here.
Context: ...TanStack Start. - TanStack Router Docs - [Neon Auth Documentation](https://neon.co...

(QB_NEW_EN)


[grammar] ~10-~10: There might be a mistake here.
Context: ...low with Neon Auth (based on Stack Auth) - SSR Compatible - Works with TanStack S...

(QB_NEW_EN)


[grammar] ~11-~11: There might be a mistake here.
Context: ...h TanStack Start's server-side rendering - Auto Database Setup - Neon Launchpad c...

(QB_NEW_EN)


[grammar] ~12-~12: There might be a mistake here.
Context: ...on Launchpad creates database connection - Modern UI - Clean interface with Tailw...

(QB_NEW_EN)


[grammar] ~32-~32: There might be a mistake here.
Context: ...`` 2. Get your Neon Auth credentials: - [Neon Launchpad](https://neon.com/docs/re...

(QB_NEW_EN)


[grammar] ~54-~54: There might be a mistake here.
Context: ...in-postgres` plugin which automatically: - Creates a Neon database connection via [...

(QB_NEW_EN)


[grammar] ~56-~56: There might be a mistake here.
Context: ... the DATABASE_URL environment variable - Handles database initialization You can...

(QB_NEW_EN)


[grammar] ~82-~82: There might be a mistake here.
Context: ...andler/sign-in**: Ensure file is named handler.$.tsx` - SSR errors: All Stack Auth components ...

(QB_NEW_EN)

examples/react/start-neon-basic/docs/BUILD_TROUBLESHOOTING.md

[grammar] ~7-~7: There might be a mistake here.
Context: ...ches. Most Likely Fix (Start here): 1. Run from monorepo root directory (`/User...

(QB_NEW_EN)


[grammar] ~33-~33: There might be a mistake here.
Context: ...ry. This is a significant issue because: - Different dependency versions might be r...

(QB_NEW_EN)


[grammar] ~38-~38: There might be a mistake here.
Context: ...ersion Conflicts (Root Cause Identified) When using a different Node.js version t...

(QB_NEW_EN)


[grammar] ~39-~39: There might be a mistake here.
Context: ... to different versions of @types/node: - The root package.json specifies `"@typ...

(QB_NEW_EN)


[grammar] ~40-~40: There might be a mistake here.
Context: ...e: - The root package.jsonspecifies"@types/node": "^22.10.2"- However, some dependencies (likemsw@2....

(QB_NEW_EN)


[grammar] ~42-~42: There might be a mistake here.
Context: ...types between @types/[email protected] and @types/[email protected] - The error manifests as: "Type 'Plugin<an...

(QB_NEW_EN)


[grammar] ~70-~70: There might be a mistake here.
Context: ...lutions (Not Verified) ### Solution 1: Use Correct Node.js Version AND pnpm Versio...

(QB_NEW_EN)


[grammar] ~150-~150: There might be a mistake here.
Context: ...ia @microsoft/api-extractor) pulls in @types/[email protected] - These create incompatible Vite plugin ty...

(QB_NEW_EN)


[grammar] ~163-~163: There might be a mistake here.
Context: ... tracker ## What I Modified File: /Users/philip/git/tanstack-router/packages/server-functions-plugin/src/index.ts - Added as any type assertions to 4 plug...

(QB_NEW_EN)


[grammar] ~189-~189: There might be a mistake here.
Context: ...ev ``` ## For Future Agents 1. Work from monorepo root for better access to al...

(QB_NEW_EN)


[grammar] ~190-~190: There might be a mistake here.
Context: ...lignment is critical** - Node.js, pnpm, and lock file must match repository 3. **Th...

(QB_NEW_EN)


[grammar] ~190-~190: There might be a mistake here.
Context: ...l** - Node.js, pnpm, and lock file must match repository 3. The TypeScript errors...

(QB_NEW_EN)


[grammar] ~192-~192: There might be a mistake here.
Context: ...hes 4. Lock file drift means you're using wrong dependency versions 5. **@types/n...

(QB_NEW_EN)

🪛 markdownlint-cli2 (0.17.2)
examples/react/start-neon-basic/README.md

69-69: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

examples/react/start-neon-basic/docs/BUILD_TROUBLESHOOTING.md

20-20: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (12)
examples/react/start-neon-basic/postcss.config.mjs (1)

1-6: LGTM: Standard Tailwind + Autoprefixer PostCSS setup

examples/react/start-neon-basic/.vscode/settings.json (1)

1-11: LGTM: Nice DX touches for generated route tree

examples/react/start-neon-basic/src/utils/neon.ts (1)

3-8: Server-only usage verified
The repository-wide search found no imports of src/utils/neon.ts in any .ts or .tsx files outside of your server code, so it remains safe as a server‐only module. Renaming to neon.server.ts or adding a runtime guard is still optional for future clarity but is not required to prevent client-side leakage.

examples/react/start-neon-basic/src/components/NotFound.tsx (1)

1-25: Component looks good and consistent with other examples.

examples/react/start-neon-basic/src/stack.ts (1)

4-7: LGTM on navigate adapter and token store choice.

Hook shape matches TanStack Router usage, and tokenStore memory/cookie split is appropriate for SSR vs browser.

examples/react/start-neon-basic/src/styles/app.css (1)

5-8: Good use of color-scheme for native UI consistency.

examples/react/start-neon-basic/src/router.tsx (2)

5-12: Router factory looks solid.


14-18: Module augmentation is correct.

examples/react/start-neon-basic/src/routes/handler.$.tsx (1)

7-9: Ensure auth handler receives query/hash, not just pathname.
OAuth/OIDC callbacks typically rely on search/hash params. Passing only pathname may break the flow. Recommend including search and hash.

-  const pathname = router.state.location.pathname;
+  const { pathname, search, hash } = router.state.location;
+  const handlerLocation = `${pathname}${search ?? ''}${hash ?? ''}`;
...
-  return <StackHandler app={stackClientApp} location={pathname} fullPage />;
+  return <StackHandler app={stackClientApp} location={handlerLocation} fullPage />;

Also applies to: 19-20

examples/react/start-neon-basic/src/components/DefaultCatchBoundary.tsx (1)

11-17: Pattern matches other examples; looks good.

examples/react/start-neon-basic/src/routes/__root.tsx (1)

151-160: Confirm the correct sign-out API usage
It’s unclear whether the signOut method should be called on the user object returned by stackClientApp.getUser() or on the stackClientApp instance itself.

Please verify and update as needed:

  • File: examples/react/start-neon-basic/src/routes/__root.tsx (lines 151–160)
    Ensure that user.signOut() actually exists and is the intended API.
  • If the SDK exposes sign-out on the client instance instead, replace:
    - await user.signOut()
    + await stackClientApp.signOut()
    to invoke the correct method.

Consult the @stackframe/react documentation to confirm which API is correct and update the implementation accordingly.

examples/react/start-neon-basic/src/routes/_authed/posts.$postId.tsx (1)

6-13: Solid route wiring and boundaries.

Loader, notFound, and error boundaries look correct and match the server fn contract.


*Note: These are simplified examples. The actual code includes additional UI components, navigation, and features.*

### Handler Route ([src/routes/handler.$.tsx](src/routes/handler.%24.tsx))
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix broken link to handler route.

Use the actual file path with $ instead of URL-encoding to %24.

-### Handler Route ([src/routes/handler.$.tsx](src/routes/handler.%24.tsx))
+### Handler Route ([src/routes/handler.$.tsx](src/routes/handler.$.tsx))
📝 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
### Handler Route ([src/routes/handler.$.tsx](src/routes/handler.%24.tsx))
### Handler Route ([src/routes/handler.$.tsx](src/routes/handler.$.tsx))
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/docs/AUTHENTICATION_TROUBLESHOOTING.md around
line 23, the handler route link is URL-encoded using %24; change the link to use
the actual dollar-sign filename segment (use handler.$.tsx) so the path reads
src/routes/handler.$.tsx instead of src/routes/handler.%24.tsx, updating the
markdown link text and target accordingly.

});
```

### Root Route ([src/routes/__root.tsx](/src/routes/__root.tsx)
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Close the markdown link parenthesis in the “Root Route” heading.

-### Root Route ([src/routes/__root.tsx](/src/routes/__root.tsx)
+### Root Route ([src/routes/__root.tsx](/src/routes/__root.tsx))
📝 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
### Root Route ([src/routes/__root.tsx](/src/routes/__root.tsx)
### Root Route ([src/routes/__root.tsx](/src/routes/__root.tsx))
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/docs/AUTHENTICATION_TROUBLESHOOTING.md around
line 51, the "Root Route" markdown heading opens a link parenthesis but doesn't
close it; fix by adding the missing closing parenthesis after the link path so
the heading becomes valid Markdown.

Comment on lines +53 to +65
import { StackProvider, StackTheme } from "@stackframe/react";
import { stackClientApp } from "../stack";

function RootComponent() {
return (
<StackProvider app={stackClientApp}>
<StackTheme>
<Outlet />
</StackTheme>
</StackProvider>
);
}
```
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add missing Outlet import in the Root example.

Without it, readers copying the snippet get a TS/runtime error.

-import { StackProvider, StackTheme } from "@stackframe/react";
+import { StackProvider, StackTheme } from "@stackframe/react";
+import { Outlet } from "@tanstack/react-router";
 import { stackClientApp } from "../stack";
📝 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
import { StackProvider, StackTheme } from "@stackframe/react";
import { stackClientApp } from "../stack";
function RootComponent() {
return (
<StackProvider app={stackClientApp}>
<StackTheme>
<Outlet />
</StackTheme>
</StackProvider>
);
}
```
import { StackProvider, StackTheme } from "@stackframe/react";
import { Outlet } from "@tanstack/react-router";
import { stackClientApp } from "../stack";
function RootComponent() {
return (
<StackProvider app={stackClientApp}>
<StackTheme>
<Outlet />
</StackTheme>
</StackProvider>
);
}
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/docs/AUTHENTICATION_TROUBLESHOOTING.md around
lines 53 to 65 the RootComponent example uses <Outlet /> but the snippet is
missing its import; add an import for Outlet (e.g., import { Outlet } from
"react-router-dom";) at the top of the snippet so the example compiles and runs
without TS/runtime errors.

Comment on lines +8 to +12
1. Run from monorepo root directory (`/Users/philip/git/tanstack-router/`)
2. Use correct Node.js version: `nvm use 20.17.0`
3. Use correct pnpm version: Check `packageManager` in root package.json
4. Reset dependencies: `git checkout pnpm-lock.yaml && rm -rf node_modules && pnpm install`
5. Build all packages: `pnpm build:all`
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Remove machine-specific absolute paths and PII.

Hard-coded local paths (e.g., /Users/philip/...) leak developer info and hinder portability. Replace with repo-agnostic placeholders.

-1. Run from monorepo root directory (`/Users/philip/git/tanstack-router/`)
+1. Run from the monorepo root directory (repo root)-**File**: `/Users/philip/git/tanstack-router/packages/server-functions-plugin/src/index.ts`
+**File**: `<repo-root>/packages/server-functions-plugin/src/index.ts`

Also applies to: 163-166

🤖 Prompt for AI Agents
In examples/react/start-neon-basic/docs/BUILD_TROUBLESHOOTING.md around lines 8
to 12 (also applies to lines 163-166), remove the hard-coded absolute path
(/Users/philip/...) and any other machine-specific or PII values and replace
them with repo-agnostic placeholders or relative references (e.g., "from the
repository root" or "<repo-root>"). Update the instructions so they reference
generic commands and locations (nvm, pnpm, git) without embedding personal file
paths, ensuring portability and no leaked developer information.

Comment on lines +7 to +11
"scripts": {
"dev": "vite dev",
"build": "vite build && tsc --noEmit",
"start": "node .output/server/index.mjs"
},
Copy link
Contributor

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Confirm dev/build commands.

If you rely on TanStack Start’s CLI output (.output/server/index.mjs), prefer its commands; otherwise verify that vite build produces .output.

   "scripts": {
-    "dev": "vite dev",
-    "build": "vite build && tsc --noEmit",
+    "dev": "tanstack start dev",
+    "build": "tanstack start build && tsc --noEmit",
     "start": "node .output/server/index.mjs"
   },

Run to confirm .output is produced by current build:


🏁 Script executed:

#!/bin/bash
pnpm -s build >/dev/null 2>&1 || true
fd -a '.output/server/index.mjs'

Length of output: 55


To accurately determine whether a .output directory and the server/index.mjs file are being generated by your current build, please run:


🏁 Script executed:

#!/bin/bash
# Run the existing build
pnpm -s build >/dev/null 2>&1 || true

# Check for the .output directory
echo "Searching for any .output directory in the repo..."
find . -type d -name ".output"

# List all files under .output (if any)
echo "Listing all files under .output (if it exists)..."
find . -type f -path "*/.output/*"

Length of output: 310


🚨 Build output missing .output directory – start script will fail

Running pnpm build produces no .output directory, so node .output/server/index.mjs will error at runtime. You must either use TanStack Start’s CLI (which generates .output) or reconfigure Vite to output there.

Please update examples/react/start-neon-basic/package.json scripts accordingly:

   "scripts": {
-    "dev": "vite dev",
-    "build": "vite build && tsc --noEmit",
+    "dev": "tanstack start dev",
+    "build": "tanstack start build && tsc --noEmit",
     "start": "node .output/server/index.mjs"
   },

Or, if you intend to stick with Vite’s native commands, adjust your vite.config.* to emit to .output/server/index.mjs.

📝 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
"scripts": {
"dev": "vite dev",
"build": "vite build && tsc --noEmit",
"start": "node .output/server/index.mjs"
},
"scripts": {
- "dev": "vite dev",
"dev": "tanstack start dev",
"build": "tanstack start build && tsc --noEmit",
"start": "node .output/server/index.mjs"
},
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/package.json around lines 7–11, the "start"
script points at .output/server/index.mjs which Vite's default build does not
produce; either switch to TanStack Start build output or align scripts with
Vite. Fix option A: if you want TanStack Start output, update scripts to run the
Start build (install and use the Start CLI) so pnpm build produces .output and
keep "start" as node .output/server/index.mjs. Fix option B: if you intend to
use Vite, change the "start" script to a Vite-compatible runtime (e.g., use
"vite preview" for serving the production build) or change your vite.config to
set build.outDir to .output and ensure the server entry is emitted; pick one
approach and make the scripts and build config consistent.

@@ -0,0 +1,8 @@
import { Pool } from '@neondatabase/serverless'
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Configure WebSocket for Node runtimes (required by @neondatabase/serverless).

You already depend on ws. Without this, Node < 20 lacks a global WebSocket and connections will fail.

-import { Pool } from '@neondatabase/serverless'
+import { Pool, neonConfig } from '@neondatabase/serverless'
+import WebSocket from 'ws'
+neonConfig.webSocketConstructor = WebSocket as unknown as typeof WebSocket
📝 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
import { Pool } from '@neondatabase/serverless'
import { Pool, neonConfig } from '@neondatabase/serverless'
import WebSocket from 'ws'
neonConfig.webSocketConstructor = WebSocket as unknown as typeof WebSocket
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/src/utils/neon.ts around line 1, the
@neondatabase/serverless Pool requires a global WebSocket in Node runtimes <20;
import the 'ws' package and assign it to the global/WebSocket before creating or
using the Pool so the library has a WebSocket implementation available at
runtime. Ensure the import and global assignment occur at the top of the file
(before any Pool instantiation) and only set global WebSocket when it is missing
to avoid overwriting native implementations.

Comment on lines +3 to +8
export function getNeonServerClient() {
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
})
return pool
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Make the Pool a singleton to avoid connection churn and HMR leaks.

Creating a new Pool on each call will reopen connections and can leak during dev HMR. Reuse a module-scoped instance (and cache on globalThis in dev).

-export function getNeonServerClient() {
-  const pool = new Pool({
-    connectionString: process.env.DATABASE_URL,
-  })
-  return pool
-}
+const globalForNeon = globalThis as unknown as { neonPool?: Pool }
+
+function createPool() {
+  const connectionString = process.env.DATABASE_URL
+  if (!connectionString) {
+    throw new Error('DATABASE_URL is not set. Copy env.example to .env and fill it in.')
+  }
+  return new Pool({ connectionString, max: 1 })
+}
+
+export const pool = globalForNeon.neonPool ?? createPool()
+if (process.env.NODE_ENV !== 'production') globalForNeon.neonPool = pool
+
+export function getNeonServerClient(): Pool {
+  return pool
+}
📝 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
export function getNeonServerClient() {
const pool = new Pool({
connectionString: process.env.DATABASE_URL,
})
return pool
}
// examples/react/start-neon-basic/src/utils/neon.ts
const globalForNeon = globalThis as unknown as { neonPool?: Pool }
function createPool() {
const connectionString = process.env.DATABASE_URL
if (!connectionString) {
throw new Error(
'DATABASE_URL is not set. Copy env.example to .env and fill it in.'
)
}
return new Pool({ connectionString, max: 1 })
}
export const pool = globalForNeon.neonPool ?? createPool()
if (process.env.NODE_ENV !== 'production') {
globalForNeon.neonPool = pool
}
export function getNeonServerClient(): Pool {
return pool
}
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/src/utils/neon.ts around lines 3 to 8, the
function creates a new Pool on every call which causes connection churn and HMR
leaks; change this to return a singleton Pool by declaring a module-scoped
variable (e.g. let pool) and initialize it only if undefined, and in development
guard also cache it on globalThis (e.g. (globalThis as any).__neonPool) so HMR
reuses the same instance; ensure getNeonServerClient returns the cached instance
and only constructs a new Pool once using process.env.DATABASE_URL.

Comment on lines +5 to +9
export type PostType = {
id: string
title: string
body: string
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Post id type mismatch with JSONPlaceholder.

JSONPlaceholder returns numeric ids; your PostType declares id: string. Either convert ids to strings on fetch or change the type. Given routes pass postId as a string, keep PostType as string and map from API.

 export type PostType = {
-  id: string
+  id: string
   title: string
   body: string
 }
+
+// Shape returned by JSONPlaceholder
+type ApiPost = {
+  id: number
+  title: string
+  body: string
+}
📝 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
export type PostType = {
id: string
title: string
body: string
}
export type PostType = {
id: string
title: string
body: string
}
// Shape returned by JSONPlaceholder
type ApiPost = {
id: number
title: string
body: string
}
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/src/utils/posts.ts around lines 5 to 9, the
PostType declares id: string but JSONPlaceholder returns numeric ids; update the
fetch logic to map the API response and convert id to a string (e.g.,
String(post.id)) so PostType stays as string and routes receiving postId as a
string continue to work, or alternatively change PostType.id to number if you
prefer to keep raw API types—implement the conversion on fetch and adjust any
downstream usages accordingly.

Comment on lines +11 to +27
export const fetchPost = createServerFn({ method: 'GET' })
.validator((d: string) => d)
.handler(async ({ data: postId }) => {
console.info(`Fetching post with id ${postId}...`)
const post = await axios
.get<PostType>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
.then((r) => r.data)
.catch((err) => {
console.error(err)
if (err.status === 404) {
throw notFound()
}
throw err
})

return post
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix 404 detection and validate empty-object responses.

  • redaxios/axios errors expose status via err.response?.status, not err.status.
  • JSONPlaceholder returns 200 with {} for missing IDs; detect that and throw notFound.
 export const fetchPost = createServerFn({ method: 'GET' })
   .validator((d: string) => d)
   .handler(async ({ data: postId }) => {
     console.info(`Fetching post with id ${postId}...`)
-    const post = await axios
-      .get<PostType>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
-      .then((r) => r.data)
-      .catch((err) => {
-        console.error(err)
-        if (err.status === 404) {
-          throw notFound()
-        }
-        throw err
-      })
-
-    return post
+    try {
+      const apiPost = await axios
+        .get<ApiPost>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
+        .then((r) => r.data)
+
+      // JSONPlaceholder returns {} for non-existent posts
+      if (!apiPost || typeof apiPost.id !== 'number') {
+        throw notFound()
+      }
+
+      const post: PostType = {
+        id: String(apiPost.id),
+        title: apiPost.title,
+        body: apiPost.body,
+      }
+      return post
+    } catch (err: any) {
+      console.error(err)
+      if (err?.response?.status === 404) {
+        throw notFound()
+      }
+      throw err
+    }
   })
📝 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
export const fetchPost = createServerFn({ method: 'GET' })
.validator((d: string) => d)
.handler(async ({ data: postId }) => {
console.info(`Fetching post with id ${postId}...`)
const post = await axios
.get<PostType>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
.then((r) => r.data)
.catch((err) => {
console.error(err)
if (err.status === 404) {
throw notFound()
}
throw err
})
return post
})
export const fetchPost = createServerFn({ method: 'GET' })
.validator((d: string) => d)
.handler(async ({ data: postId }) => {
console.info(`Fetching post with id ${postId}...`)
try {
const apiPost = await axios
.get<ApiPost>(`https://jsonplaceholder.typicode.com/posts/${postId}`)
.then((r) => r.data)
// JSONPlaceholder returns {} for non-existent posts
if (!apiPost || typeof apiPost.id !== 'number') {
throw notFound()
}
const post: PostType = {
id: String(apiPost.id),
title: apiPost.title,
body: apiPost.body,
}
return post
} catch (err: any) {
console.error(err)
if (err?.response?.status === 404) {
throw notFound()
}
throw err
}
})
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/src/utils/posts.ts around lines 11 to 27, the
error handling and response validation are wrong: redaxios/axios exposes HTTP
status on err.response?.status (not err.status), so update the catch to inspect
err.response?.status and throw notFound() on 404; additionally, after receiving
r.data, check for an empty-object response (e.g., post is null/undefined or
Object.keys(post).length === 0) and throw notFound() before returning; keep
logging the error but rethrow non-404 errors.

Comment on lines +29 to +37
export const fetchPosts = createServerFn({ method: 'GET' }).handler(
async () => {
console.info('Fetching posts...')
await new Promise((r) => setTimeout(r, 1000))
return axios
.get<Array<PostType>>('https://jsonplaceholder.typicode.com/posts')
.then((r) => r.data.slice(0, 10))
},
)
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Map ids to strings for list fetch; preserve consistent PostType.

Also prefer await for readability and consistent error stacks.

 export const fetchPosts = createServerFn({ method: 'GET' }).handler(
   async () => {
     console.info('Fetching posts...')
     await new Promise((r) => setTimeout(r, 1000))
-    return axios
-      .get<Array<PostType>>('https://jsonplaceholder.typicode.com/posts')
-      .then((r) => r.data.slice(0, 10))
+    const data = await axios
+      .get<Array<ApiPost>>('https://jsonplaceholder.typicode.com/posts')
+      .then((r) => r.data)
+    return data.slice(0, 10).map(
+      (p): PostType => ({
+        id: String(p.id),
+        title: p.title,
+        body: p.body,
+      }),
+    )
   },
 )
📝 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
export const fetchPosts = createServerFn({ method: 'GET' }).handler(
async () => {
console.info('Fetching posts...')
await new Promise((r) => setTimeout(r, 1000))
return axios
.get<Array<PostType>>('https://jsonplaceholder.typicode.com/posts')
.then((r) => r.data.slice(0, 10))
},
)
export const fetchPosts = createServerFn({ method: 'GET' }).handler(
async () => {
console.info('Fetching posts...')
await new Promise((r) => setTimeout(r, 1000))
const data = await axios
.get<Array<ApiPost>>('https://jsonplaceholder.typicode.com/posts')
.then((r) => r.data)
return data.slice(0, 10).map(
(p): PostType => ({
id: String(p.id),
title: p.title,
body: p.body,
}),
)
},
)
🤖 Prompt for AI Agents
In examples/react/start-neon-basic/src/utils/posts.ts around lines 29 to 37, the
fetchPosts handler returns posts whose id fields are numbers and uses .then
chaining; update it to await the axios GET, cast/transform each returned post to
the local PostType by converting id to a string (e.g. post.id = String(post.id))
so list items have consistent string ids, and return only the first 10 items;
preserve typing as Array<PostType> and use try/catch or let the function
propagate errors (but use await for readability and proper stack traces).

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