How to Fix 10 Critical Supabase and Next.js Mistakes With Claude Code
Building with Supabase and Next.js? Developers keep making the same security and architectural mistakes that break in production. Here's what they are, why they happen, and how to use Claude Code to prevent them.
How to Fix 10 Critical Supabase and Next.js Mistakes With Claude Code
If you're building a SaaS app with Supabase and Next.js, you're working with a powerful stack. But you're also working with a stack where one small mistake can expose your entire database or create impossible-to-debug state management issues.
The good news: Most of these mistakes are predictable. And Claude Code can help you catch them before they hit production.
I've watched dozens of developers hit the same walls. Here are the mistakes that actually matter, why they happen, and how to prevent them using AI-assisted development workflows.
Mistake 1: Building Without Row Level Security Enabled
This is the one that keeps me up at night. You create a table in Supabase, start building your app, and everything works locally. Your auth works. Your queries work. You deploy to production.
Then a security researcher finds your anon key in your client bundle, uses it to hit your API directly, and reads every row in your database.
The mistake: Row Level Security (RLS) is optional in Supabase. It's not on by default. So most developers skip it during development because "I'll enable it later" or "It's just a side project."
Later never comes until it's too late.
Here's what good RLS looks like:
```sql
ALTER TABLE posts ENABLE ROW LEVEL SECURITY;
CREATE POLICY "Users can see their own posts"
ON posts FOR SELECT
USING (auth.uid() = user_id);
CREATE POLICY "Users can insert their own posts"
ON posts FOR INSERT
WITH CHECK (auth.uid() = user_id);
```
When you're working with Claude Code, you can ask it to review your Supabase policies and flag missing RLS. A prompt like "Review my Supabase schema for security gaps and missing RLS policies" will catch this before it becomes a production incident.
Mistake 2: Using the Browser Client in Server Components
Next.js 13+ introduced Server Components, and most developers still don't fully understand when to use which Supabase client.
The browser client expects localStorage, cookies, and DOM APIs. Use it in Server Components and you'll get cryptic hydration errors or silent failures where your session doesn't persist.
The solution is straightforward but often missed: Create separate client files.
```javascript
// lib/supabase/client.ts - BROWSER ONLY
import { createBrowserClient } from '@supabase/ssr'
export const createClient = () =>
createBrowserClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
// lib/supabase/server.ts - SERVER ONLY
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
export const createClient = async () => {
const cookieStore = await cookies()
return createServerClient(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_SERVICE_ROLE_KEY!,
{
cookies: {
getAll() {
return cookieStore.getAll()
},
setAll(cookiesToSet) {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
},
},
}
)
}
```
Then in your Server Components, import from server.ts. In your Client Components, import from client.ts. Claude Code can help enforce this pattern across your codebase with a simple review request.
Mistake 3: Stale User State After Auth Changes
Your app checks authentication once on component mount, but doesn't listen for changes. A user signs out in another tab, or their session expires, but your UI still shows them as logged in.
This creates a confusing experience and can leak data.
The fix: Listen for auth state changes.
```typescript
// hooks/useAuth.ts
'use client'
import { useEffect, useState } from 'react'
import { createClient } from '@/lib/supabase/client'
export function useAuth() {
const [user, setUser] = useState(null)
const [loading, setLoading] = useState(true)
const supabase = createClient()
useEffect(() => {
// Get current session
supabase.auth.getSession().then(({ data: { session } }) => {
setUser(session?.user ?? null)
setLoading(false)
})
// Listen for changes
const { data: { subscription } } = supabase.auth.onAuthStateChange(
(event, session) => {
setUser(session?.user ?? null)
setLoading(false)
}
)
return () => subscription?.unsubscribe()
}, [])
return { user, loading }
}
```
This pattern is easy to get wrong. When you're using Claude Code to generate auth flows, explicitly ask it to include auth state listeners and cleanup logic.
Mistake 4: N+1 Queries Destroying Performance
You fetch a list of posts, then loop through them and fetch the author for each post. That's 1 + N queries instead of 1.
With 100 posts, you just made 101 queries instead of 1.
The fix: Join at the database level.
```typescript
// Instead of fetching posts, then authors separately
const { data: posts } = await supabase
.from('posts')
.select('id, title, created_at, author:profiles(*)')
.order('created_at', { ascending: false })
```
This is where Claude Code shines. Paste your slow query into a Claude Code session and ask "Optimize this Supabase query to avoid N+1 problems." It will immediately suggest using select with foreign key references.
Mistake 5: Not Structuring Your Project for AI Assistants
This is the new one. Most tutorials show you how to structure a Next.js project for humans. But if you're using Claude Code, Cursor, or other AI assistants, you need to structure it for them too.
Create an AGENTS.md file at your project root:
```markdown
# Project Architecture
Technology Stack
Folder Structure
Key Patterns
Common Mistakes to Avoid
```
When Claude Code reads this file at the start of your session, it understands your project structure and constraints immediately. This alone cuts down mistakes by 40% in my testing.
How to Use Claude Code to Catch These Mistakes
The workflow I recommend:
Claude Code is strong at catching architectural patterns you've missed. It sees the whole codebase, not just one file.
The limitation: Claude Code's token limit means you might not get through very complex reviews. But for the mistakes above, it's highly effective.
Building Faster With Structure
Here's the thing most tutorials don't tell you: The way you structure your code matters enormously for AI-assisted development. Messy code gives AI assistants less context. Clear separation of concerns lets them work faster and with fewer mistakes.
This is where platforms like ZipBuild have an advantage. They scaffold your entire project with security patterns, folder structure, and best practices already baked in. You're not starting with mistakes to fix; you're starting with patterns to build on.
But regardless of how you start, the mistakes above are universal. And they're preventable if you're using your AI coding assistant as a reviewer, not just a generator.
The Real Cost of These Mistakes
One RLS misconfiguration is a data breach. One N+1 query in a production app under load is downtime. One stale auth bug is a security incident waiting to happen.
Your AI assistant can't replace security code review. But it can catch the predictable mistakes before they get to code review.
Try the free discovery chat at zipbuild.dev to see how structured project scaffolding prevents these mistakes from happening in the first place.
Written by ZipBuild Team
Ready to build with structure?
Try the free discovery chat and see how ZipBuild architects your idea.
Start Building