Browse 225 rules, 42 knowledge articles, and 28 prompt templates across security, performance, architecture, and quality.
225 rules
Using 'as' type assertions on external input (params, form data, request bodies) provides zero runtime safety. Use Zod for runtime validation. [CWE-20 · A03:2021]
Destructuring { data } without checking { error } from Supabase queries ignores failures silently. When error is non-null, data is always null. [CWE-252]
Treating all Supabase errors the same (if error, throw) hides whether a record is missing or the query itself failed. Check error codes for proper handling.
Using .select('*') fetches all columns including large text/json fields, wastes bandwidth, leaks data shape, and prevents index-only scans.
Fetching parent records then looping to fetch children creates N+1 HTTP requests. Use Supabase nested .select('*, children(*)') to resolve in a single query. [CWE-400]
Filtering or ordering on unindexed columns causes full table scans. RLS policy columns like user_id and org_id especially need indexes. [CWE-405]
Using .range() offset pagination for large datasets forces PostgreSQL to scan all skipped rows. Use cursor-based pagination with .gt()/.lt() for constant-time page fetches.
Each serverless invocation opening a direct database connection exhausts PostgreSQL's connection limit. Use Supavisor pooler URLs for all serverless environments.
Inserting or updating rows one at a time in a loop creates N HTTP requests. Use .insert([...]) or .upsert([...]) to batch into a single request.
Hand-writing TypeScript interfaces for database tables leads to drift between code and schema. Use supabase gen types to generate types automatically.
Migration files without proper structure (table, indexes, RLS, policies, comments) are harder to review and prone to missing critical steps like RLS.
Using the wrong Supabase client for the context breaks RLS, leaks auth state, or causes hydration errors. Match client type to Next.js rendering context.
Spreading or Object.assign-ing untrusted user input into objects can pollute Object.prototype and lead to security bypasses. [CWE-1321]
Math.random() is not cryptographically secure. Use crypto.randomUUID() or crypto.getRandomValues() for tokens, IDs, and security-sensitive values. [CWE-338]
Using eval(), new Function(), or innerHTML with user-controlled strings enables arbitrary code execution in the browser. [CWE-95 · A03:2021]
Using dangerouslySetInnerHTML with unsanitized user input enables XSS attacks. Always sanitize with DOMPurify or a trusted library. [CWE-79 · A03:2021]
Missing cleanup in useEffect for subscriptions, timers, event listeners, and AbortControllers causes memory leaks, stale callbacks, and state updates on unmounted components.
Using array index as key in lists that can be reordered, filtered, or inserted into causes React to mismap state to the wrong items, creating subtle and hard-to-debug UI bugs.
Without error boundaries, a single component crash unmounts the entire React tree. Wrap unreliable sections so failures are isolated and recoverable.
Switching between controlled (value prop) and uncontrolled (defaultValue/no value) patterns on the same input causes React warnings and unpredictable behavior.
Creating new objects, arrays, or functions inline in JSX causes child components to re-render on every parent render due to referential inequality.
Rendering thousands of DOM nodes for long lists causes slow initial render, high memory usage, and scroll jank. Use virtualization (react-window, TanStack Virtual).
Running expensive calculations (sorting, filtering, transforming large datasets) on every render wastes CPU cycles. Use useMemo to cache results.
Putting too much state in a single React Context causes all consumers to re-render when any value changes. Split into focused contexts.
BeforeMerge scans your pull requests against these rules automatically. Get actionable feedback before code ships to production.