skills/supabase_patterns/SKILL.md
Guides implementation of Next.js 15 App Router features with Supabase SSR. Helps choose between Server/Client Components, select correct Supabase client, and follow security patterns. Use when building pages, components, or API routes.
npx skillsauth add vuralserhat86/antigravity-agentic-skills supabase_patternsInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Interactive guide for implementing features using patterns from .claude/modules/nextjs-patterns.md and .claude/modules/supabase-security.md.
Use this decision tree to choose the right component type:
Step 1: Does it need user interaction?
→ YES = Client Component ('use client')
→ NO = Continue to Step 2
Step 2: Does it fetch data from database/API?
→ YES = Server Component (default) → NO = Server Component (default, unless Step 1 was yes)
| What You're Building | Component Type | Supabase Client |
|---------------------|----------------|-----------------|
| Page that displays recipes | Server Component | @/lib/supabase/server |
| Save/delete button | Client Component | @/lib/supabase/client |
| Form with validation | Client Component | @/lib/supabase/client |
| Form with Server Action | Server Component | Use Server Action |
| Real-time chat | Client Component | @/lib/supabase/client |
| Dashboard with data | Server Component | @/lib/supabase/server |
| API route handler | Server (Route Handler) | @/lib/supabase/server |
When: Displaying data from database
File location: app/**/page.tsx or app/**/layout.tsx
// app/recipes/page.tsx
import { createClient } from '@/lib/supabase/server'
import RecipeList from '@/components/RecipeList'
export default async function RecipesPage() {
const supabase = await createClient()
// Fetch data on server
const { data: recipes, error } = await supabase
.from('saved_recipes')
.select('*')
// Handle errors
if (error) {
return <ErrorDisplay message="Failed to load recipes" />
}
// Pass data to components
return <RecipeList recipes={recipes} />
}
See: nextjs-patterns.md
When: User interactions, state management
File location: components/**/*.tsx
// components/SaveButton.tsx
'use client'
import { createClient } from '@/lib/supabase/client'
import { useState } from 'react'
export default function SaveButton({ recipeId }: { recipeId: string }) {
const [saving, setSaving] = useState(false)
const supabase = createClient()
const handleSave = async () => {
setSaving(true)
const { error } = await supabase
.from('saved_recipes')
.insert({ id: recipeId })
if (error) {
console.error('Save failed:', error)
}
setSaving(false)
}
return (
<button onClick={handleSave} disabled={saving}>
{saving ? 'Saving...' : 'Save Recipe'}
</button>
)
}
See: nextjs-patterns.md
When: Need to pass server data to interactive components Rule: Only pass required fields, never full objects
// app/profile/page.tsx (Server Component)
import { createClient } from '@/lib/supabase/server'
import ProfileEditor from '@/components/ProfileEditor' // Client Component
export default async function ProfilePage() {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) {
redirect('/login')
}
// ✅ CORRECT: Pass only required fields
return (
<ProfileEditor
userId={user.id}
email={user.email}
name={user.user_metadata?.name}
/>
)
// ❌ WRONG: Don't pass full user object (security risk)
// return <ProfileEditor user={user} />
}
See: supabase-security.md
When: Form submissions, data mutations
File location: app/actions.ts or colocated with page
// app/actions.ts
'use server'
import { createClient } from '@/lib/supabase/server'
import { revalidatePath } from 'next/cache'
export async function saveRecipe(formData: FormData) {
const supabase = await createClient()
// Validate user
const { data: { user } } = await supabase.auth.getUser()
if (!user) {
return { error: 'Unauthorized' }
}
// Extract form data
const name = formData.get('name') as string
const ingredients = formData.get('ingredients') as string
// Perform mutation
const { error } = await supabase
.from('saved_recipes')
.insert({ name, ingredients, user_id: user.id })
if (error) {
return { error: error.message }
}
// Revalidate cache
revalidatePath('/recipes')
return { success: true }
}
See: nextjs-patterns.md
Critical rule: Use the correct client for the environment.
| Environment | Import | Usage |
|------------|--------|-------|
| Server Component | import { createClient } from '@/lib/supabase/server' | const supabase = await createClient() |
| Client Component | import { createClient } from '@/lib/supabase/client' | const supabase = createClient() |
| Server Action | import { createClient } from '@/lib/supabase/server' | const supabase = await createClient() |
| Route Handler | import { createClient } from '@/lib/supabase/server' | const supabase = await createClient() |
| Middleware | import { createClient } from '@/lib/supabase/middleware' | const { supabase } = createClient(request) |
See: supabase-security.md
Before implementing, verify:
getUser() not getSession() for auth validation?See: supabase-security.md
| ❌ Mistake | ✅ Fix |
|-----------|--------|
| Async Client Component | Use Server Component or useEffect |
| Wrong Supabase client | Check table above |
| Passing full user object | Pass only needed fields |
| any types | Use specific types from lib/supabase/types.ts |
| No error handling | Always check for errors |
| Missing loading states | Show spinner/skeleton |
See: anti-patterns.md
Kaynak: Supabase SSR Guide (Next.js) & Next.js 15 App Router Security
@supabase/ssr kullanarak Server Client (RSC/Actions) veya Browser Client (RCC) başlat.database.types.ts dosyasını otomatik generate et ve Supabase instance'ına enjekte et.use server direktifiyle güvenli aksiyonlar tanımla ve revalidatePath ile cache'i güncelle.auth.uid() bazlı politikaların tüm tablolar için (SELECT/INSERT/UPDATE) aktif olduğunu doğrula.getSession() yerine her zaman daha güvenli olan getUser() metodunu tercih et.supabase.channel() üzerinden subscription'ları kur ve temizle (unmount).| Aşama | Doğrulama |
|-------|-----------|
| 1 | Service Role Key asla client-side kodda (RCC) kullanılıyor mu? |
| 2 | Form submit sonrası "Optimistic UI" güncellemeleri yapıldı mı? |
| 3 | process.env verileri hem local hem de üretim (Supabase Dashboard) ortamında uyumlu mu? |
Supabase Patterns v2.0 - With Workflow
For detailed patterns and examples:
Server Component template:
import { createClient } from '@/lib/supabase/server'
export default async function Page() {
const supabase = await createClient()
const { data, error } = await supabase.from('table').select('*')
if (error) return <Error />
return <Component data={data} />
}
Client Component template:
'use client'
import { createClient } from '@/lib/supabase/client'
import { useState } from 'react'
export default function Component() {
const [state, setState] = useState()
const supabase = createClient()
// Your interactive logic
return <div onClick={handler}>...</div>
}
Server Action template:
'use server'
import { createClient } from '@/lib/supabase/server'
import { revalidatePath } from 'next/cache'
export async function action(formData: FormData) {
const supabase = await createClient()
const { data: { user } } = await supabase.auth.getUser()
if (!user) return { error: 'Unauthorized' }
// Mutation logic
revalidatePath('/path')
return { success: true }
}
tools
Production-tested setup for Zustand state management in React. Includes patterns for persistence, devtools, and TypeScript patterns. Prevents hydration mismatches and render loops.
development
Comprehensive spreadsheet creation, editing, and analysis with support for formulas, formatting, data analysis, and visualization. When Claude needs to work with spreadsheets (.xlsx, .xlsm, .csv, .tsv, etc) for: (1) Creating new spreadsheets with formulas and formatting, (2) Reading or analyzing data, (3) Modify existing spreadsheets while preserving formulas, (4) Data analysis and visualization in spreadsheets, or (5) Recalculating formulas
development
--- name: websocket_engineer router_kit: FullStackKit description: WebSocket specialist for real-time communication systems. Invoke for Socket.IO, WebSocket servers, bidirectional messaging, presence systems. Keywords: WebSocket, Socket.IO, real-time, pub/sub, Redis. triggers: - WebSocket - Socket.IO - real-time communication - bidirectional messaging - pub/sub - server push - live updates - chat systems - presence tracking role: specialist scope: implementation output-format:
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs.