How to Structure Next.js Projects for AI Integration: Claude Code, Supabase, and Production Patterns
Building AI features into Next.js apps requires more than adding API calls. We cover the architectural decisions that separate prototype code from production systems, including how to structure Claude Code workflows, implement Supabase security correctly, and avoid the common mistakes that cost developers hours of debugging.
The Problem: Most Developers Structure AI Features Wrong
You've probably experienced this: You add an LLM integration to your Next.js app by calling Claude from a route handler, maybe throwing in a Supabase query, and it works. Ship to production. Everything looks fine until you're scattered all over the codebase — LLM calls in components, in Server Actions, in utility files, auth logic mixed with business logic, and no clear pattern for where AI workflows actually belong.
By the time you need to modify how your app talks to Claude, handle token limits, or scale from one AI feature to five, your codebase is a maintenance nightmare.
The reality in 2026 is that roughly 70% of new production applications need some form of LLM integration. Whether it's a chat interface, document processing, autonomous workflows, or AI-assisted forms, the architectural decision of how to structure AI code is now as important as choosing between CSR and SSR.
This post walks through the production-ready Next.js structure that actually works, the Claude Code workflows that prevent token burnout, and the Supabase security patterns that protect your data.
Structure Your Next.js App for AI From Day One
The modern Next.js project structure for AI integration builds on the App Router foundation but adds intentional directories for AI-specific concerns:
```
src/
├── app/
│ ├── api/
│ │ ├── chat/
│ │ │ └── route.ts # Chat endpoints only
│ │ ├── documents/
│ │ │ └── route.ts # Document processing endpoints
│ │ └── webhooks/
│ │ └── route.ts # External service webhooks
│ ├── (auth)/
│ │ ├── login/
│ │ └── signup/
│ ├── dashboard/
│ │ └── page.tsx # Protected routes
│ └── layout.tsx
├── agents/ # NEW: AI workflow orchestration
│ ├── chat-agent.ts
│ ├── document-processor.ts
│ └── autonomous-workflows.ts
├── lib/
│ ├── ai/
│ │ ├── claude-client.ts # Claude configuration and utilities
│ │ ├── prompts.ts # Centralized prompt templates
│ │ └── token-counter.ts # Track token usage
│ ├── database/
│ │ ├── supabase-client.ts
│ │ └── rls-policies.sql # RLS security rules
│ ├── auth/
│ │ └── middleware.ts
│ └── hooks/
│ └── use-user.ts # Auth context hook
├── types/
│ └── ai.ts # Claude request/response types
└── env.local # Claude API key, Supabase URL
```
The key distinction: API routes handle HTTP requests and serialization. The /agents directory contains the actual AI orchestration logic — this is where you write multi-step workflows, manage context windows, and handle long-running processes. Your components stay thin; they call either route handlers or Server Actions that delegate to agents.
This separation prevents the tangle of LLM code scattered through your project. When you need to change how Claude is invoked, you modify one place. When token costs explode, you can see exactly what's consuming context.
Use the /context Command to Control Token Burn
One developer reported: "One complex prompt to Claude and by the end you've burned 50-70% of your 5-hour limit. Two prompts and you're done for the week."
This is the hidden killer of Claude Code workflows. Each message includes the full context of your codebase, your project history, and the files you've touched. On large codebases or long sessions, context bloat becomes your enemy.
The solution: Use the /context command inside Claude Code sessions to explicitly control what gets included.
Before starting a complex feature:
This workflow change alone can extend your Claude Code time from burning out in two prompts to handling a full day of development. The multi-agent pattern (running Opus for complex reasoning and delegating focused tasks to Sonnet) multiplies your effective token budget even further.
Secure Supabase With Row Level Security (RLS)
A critical mistake: Creating a Supabase table without enabling Row Level Security. It works fine in dev. Then you deploy and discover anyone with your anon key can read, modify, or delete ALL data in that table.
This isn't a minor oversight — it's a production security vulnerability.
In your Supabase dashboard, every table needs RLS policies. Here's the pattern:
```sql
-- Enable RLS on the table
ALTER TABLE profiles ENABLE ROW LEVEL SECURITY;
-- Users can only read their own profile
CREATE POLICY "Users can read own profile"
ON profiles FOR SELECT
USING (auth.uid() = user_id);
-- Users can only update their own profile
CREATE POLICY "Users can update own profile"
ON profiles FOR UPDATE
USING (auth.uid() = user_id);
-- Prevent deletion (optional)
CREATE POLICY "No profile deletion"
ON profiles FOR DELETE
USING (false);
```
Then, in your Next.js Server Actions, you never need to check auth manually — Supabase enforces RLS at the database level:
```typescript
export async function getProfile() {
const supabase = createClient();
// RLS automatically filters to current user
const { data, error } = await supabase
.from('profiles')
.select('*')
.single();
return data;
}
```
If someone tries to access another user's data or an unauthenticated request comes in, Supabase silently returns nothing. The protection happens at the database boundary, not in your application code.
Solve Auth Middleware Performance Issues
Supabase Auth middleware can create lag, especially on unguarded routes that still need to check authentication status. The naive solution (checking auth on every route) burns performance.
Instead, use a matcher pattern with route-specific logic:
```typescript
// middleware.ts
import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
export function middleware(request: NextRequest) {
const publicRoutes = ['/login', '/signup', '/pricing'];
const isPublic = publicRoutes.some(route =>
request.nextUrl.pathname.startsWith(route)
);
if (isPublic) {
return NextResponse.next();
}
// Only check auth on protected routes
const sessionToken = request.cookies.get('auth-token');
if (!sessionToken) {
return NextResponse.redirect(new URL('/login', request.url));
}
return NextResponse.next();
}
export const config = {
matcher: [
'/((?!_next/static|_next/image|favicon.ico|public).*)',
],
};
```
This prevents Supabase auth checks on routes that don't need them, reducing latency for your entire app.
Connect It All: The Production Pattern
In production, this is how it flows:
ZipBuild handles scaffolding this entire structure for you — generating the directories, security policies, and Claude integration patterns so you start with the right foundation instead of discovering these problems after deploying.
Start With Structure, Scale With Confidence
The difference between prototype code and production systems isn't more features — it's architecture. By structuring your Next.js app for AI from day one, managing Claude Code context strategically, and securing Supabase with RLS, you avoid the refactoring nightmare that catches most teams six months in.
Try the free discovery chat at zipbuild.dev to see how AI-powered scaffolding can generate this structure for your specific project, complete with Supabase security policies and Claude integration patterns already built in.
Written by ZipBuild Team
Ready to build with structure?
Try the free discovery chat and see how ZipBuild architects your idea.
Start Building