supabase — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited supabase (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
You are a Supabase expert for Next.js applications using the App Router.
createServerClient from @supabase/ssr reads cookies via Next.js cookies(); it never leaks tokens to the browsercreateBrowserClient manages the session in the browser; it must never be used in Server Components or Route HandlersSUPABASE_SERVICE_ROLE_KEY (never NEXT_PUBLIC_)supabase migration new and commit SQL files to version controlsupabase.auth.getUser() in middleware.ts on every request to keep the session cookie freshInstall the required packages:
npm install @supabase/supabase-js @supabase/ssrServer Component / Route Handler client — reads and writes cookies via Next.js cookies():
// lib/supabase/server.ts
import { createServerClient } from '@supabase/ssr'
import { cookies } from 'next/headers'
import type { Database } from '@/types/supabase'
export async function createClient() {
const cookieStore = await cookies()
return createServerClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!,
{
cookies: {
getAll() { return cookieStore.getAll() },
setAll(cookiesToSet) {
try {
cookiesToSet.forEach(({ name, value, options }) =>
cookieStore.set(name, value, options)
)
} catch {} // Safe to ignore in Server Components; middleware handles refresh
},
},
}
)
}Client Component client — manages the session in the browser:
// lib/supabase/client.ts
import { createBrowserClient } from '@supabase/ssr'
import type { Database } from '@/types/supabase'
export function createClient() {
return createBrowserClient<Database>(
process.env.NEXT_PUBLIC_SUPABASE_URL!,
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
)
}Read reference/auth-patterns.md for the full middleware setup, sign-in/sign-up flows, OAuth, and protected route patterns.
Always import your generated types for full type safety:
import type { Database } from '@/types/supabase'
// Generate: npx supabase gen types typescript --local > types/supabase.tsCommon query patterns:
const supabase = await createClient()
// Select with filter
const { data, error } = await supabase
.from('posts')
.select('id, title, created_at')
.eq('user_id', userId)
.order('created_at', { ascending: false })
// Insert
const { data, error } = await supabase
.from('posts')
.insert({ title, body, user_id: userId })
.select()
.single()
// Upsert
const { error } = await supabase
.from('profiles')
.upsert({ id: userId, display_name: name })Read reference/database-patterns.md for RLS policy patterns, migrations, RPC functions, joins, and views.
-- Authenticated users can read all rows
CREATE POLICY "authenticated read" ON posts
FOR SELECT TO authenticated USING (true);
-- Users can only modify their own rows
CREATE POLICY "owner write" ON posts
FOR ALL TO authenticated USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);
-- Role-based access using JWT claims
CREATE POLICY "admin only" ON admin_logs
FOR SELECT TO authenticated
USING (auth.jwt() ->> 'role' = 'admin');Read reference/database-patterns.md for full RLS patterns including multi-tenancy, team-based access, and helper functions.
// Upload a file
const { data, error } = await supabase.storage
.from('avatars')
.upload(`${userId}/avatar.png`, file, { upsert: true })
// Get a public URL (public bucket)
const { data: { publicUrl } } = supabase.storage
.from('avatars')
.getPublicUrl(`${userId}/avatar.png`)
// Get a signed URL (private bucket, expires in 60 seconds)
const { data, error } = await supabase.storage
.from('documents')
.createSignedUrl(`${userId}/file.pdf`, 60)Read reference/realtime-storage.md for image transformations, resumable uploads, and storage RLS.
'use client'
// Subscribe to table changes
const channel = supabase
.channel('posts-changes')
.on('postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'posts' },
(payload) => setItems(prev => [payload.new as Post, ...prev])
)
.subscribe()
return () => { supabase.removeChannel(channel) }Read reference/realtime-storage.md for presence, broadcast, and channel cleanup patterns.
@supabase/ssr client handles sessions via HttpOnly cookies; localStorage is not accessible server-side and is vulnerable to XSSgetUser() instead; getSession() does not revalidate the token against the Supabase server@supabase/ssr; the base client does not integrate with Next.js cookie handlingreference/auth-patterns.md — Sign up, sign in, OAuth, magic link, middleware, protected routes, RBACreference/database-patterns.md — Schema design, RLS policies, migrations, RPC functions, joins, viewsreference/realtime-storage.md — Realtime subscriptions, presence, broadcast, storage upload/download, signed URLs@supabase/ssr docs: https://supabase.com/docs/guides/auth/server-side/nextjs~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.