-
-
Notifications
You must be signed in to change notification settings - Fork 1.2k
feat: add start-neon-basic example with Neon Auth/Launchpad integration #5030
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
WalkthroughAdds 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
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
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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
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 unit tests
🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this 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 matchesUse 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 patternThis 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 trackedPrevents accidental commits of environment files across modes.
.env +.env.* +!.env.example
20-21
: Also ignore the generated route tree for consistency with Prettier/VSCodeAvoids 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 useproperty
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 constMinor: 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 forrel="manifest"
, and#fffff
has 5 digits. Use a meta theme-color and dropcolor
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
: Replaceany
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.
📒 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 (like
msw@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 setupexamples/react/start-neon-basic/.vscode/settings.json (1)
1-11
: LGTM: Nice DX touches for generated route treeexamples/react/start-neon-basic/src/utils/neon.ts (1)
3-8
: Server-only usage verified
The repository-wide search found no imports ofsrc/utils/neon.ts
in any.ts
or.tsx
files outside of your server code, so it remains safe as a server‐only module. Renaming toneon.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 thesignOut
method should be called on theuser
object returned bystackClientApp.getUser()
or on thestackClientApp
instance itself.Please verify and update as needed:
- File:
examples/react/start-neon-basic/src/routes/__root.tsx
(lines 151–160)
Ensure thatuser.signOut()
actually exists and is the intended API.- If the SDK exposes sign-out on the client instance instead, replace:
to invoke the correct method.- await user.signOut() + await stackClientApp.signOut()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)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
### 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
### 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.
import { StackProvider, StackTheme } from "@stackframe/react"; | ||
import { stackClientApp } from "../stack"; | ||
|
||
function RootComponent() { | ||
return ( | ||
<StackProvider app={stackClientApp}> | ||
<StackTheme> | ||
<Outlet /> | ||
</StackTheme> | ||
</StackProvider> | ||
); | ||
} | ||
``` |
There was a problem hiding this comment.
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.
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.
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` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
"scripts": { | ||
"dev": "vite dev", | ||
"build": "vite build && tsc --noEmit", | ||
"start": "node .output/server/index.mjs" | ||
}, |
There was a problem hiding this comment.
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.
"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' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
export function getNeonServerClient() { | ||
const pool = new Pool({ | ||
connectionString: process.env.DATABASE_URL, | ||
}) | ||
return pool | ||
} |
There was a problem hiding this comment.
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.
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.
export type PostType = { | ||
id: string | ||
title: string | ||
body: string | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
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 | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
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.
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)) | ||
}, | ||
) |
There was a problem hiding this comment.
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.
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).
Please review, nitpicking is encouraged :)
Summary by CodeRabbit