[{"data":1,"prerenderedAt":327},["ShallowReactive",2],{"blog-how-to/test-supabase-rls":3},{"id":4,"title":5,"body":6,"category":308,"date":309,"dateModified":309,"description":310,"draft":311,"extension":312,"faq":313,"featured":311,"headerVariant":314,"image":313,"keywords":313,"meta":315,"navigation":316,"ogDescription":317,"ogTitle":313,"path":318,"readTime":313,"schemaOrg":319,"schemaType":320,"seo":321,"sitemap":322,"stem":323,"tags":324,"twitterCard":325,"__hash__":326},"blog/blog/how-to/test-supabase-rls.md","How to Test Supabase RLS Policies",{"type":7,"value":8,"toc":291},"minimark",[9,13,17,21,27,32,35,55,68,81,85,88,97,106,121,125,128,134,138,141,182,191,195,221,227,253,272],[10,11],"category-badge",{"category":12},"How-To Guide",[14,15,5],"h1",{"id":16},"how-to-test-supabase-rls-policies",[18,19,20],"p",{},"Verify your Row Level Security is working correctly",[22,23,24],"tldr",{},[18,25,26],{},"TL;DR:\nTest RLS in the Supabase SQL Editor by setting\nrequest.jwt.claims.sub\nto simulate different users. Verify that users can only see their own data and cannot access other users' data. Test all four operations: SELECT, INSERT, UPDATE, DELETE. Also test from your actual app using DevTools.",[28,29,31],"h2",{"id":30},"method-1-sql-editor-testing","Method 1: SQL Editor Testing",[18,33,34],{},"The Supabase SQL Editor lets you simulate authenticated users:",[36,37,39,44],"step",{"number":38},"1",[40,41,43],"h3",{"id":42},"get-user-ids-for-testing","Get user IDs for testing",[45,46,51],"pre",{"className":47,"code":49,"language":50},[48],"language-text","-- List all users and their IDs\nSELECT id, email, created_at\nFROM auth.users\nORDER BY created_at DESC\nLIMIT 10;\n","text",[52,53,49],"code",{"__ignoreMap":54},"",[36,56,58,62],{"number":57},"2",[40,59,61],{"id":60},"simulate-a-specific-user","Simulate a specific user",[45,63,66],{"className":64,"code":65,"language":50},[48],"-- Set the current user for testing\nSET request.jwt.claims.sub = 'user-uuid-here';\n\n-- Now queries will run as if this user is authenticated\nSELECT * FROM todos;\n",[52,67,65],{"__ignoreMap":54},[36,69,71,75],{"number":70},"3",[40,72,74],{"id":73},"test-access-controls","Test access controls",[45,76,79],{"className":77,"code":78,"language":50},[48],"-- As User A, try to see their own data\nSET request.jwt.claims.sub = 'user-a-uuid';\nSELECT * FROM todos; -- Should show User A's todos\n\n-- Try to see User B's data directly\nSELECT * FROM todos WHERE user_id = 'user-b-uuid';\n-- Should return empty (RLS blocks it)\n\n-- Try to update User B's data\nUPDATE todos SET completed = true WHERE user_id = 'user-b-uuid';\n-- Should update 0 rows\n\n-- Reset when done\nRESET request.jwt.claims.sub;\n",[52,80,78],{"__ignoreMap":54},[28,82,84],{"id":83},"method-2-browser-devtools-testing","Method 2: Browser DevTools Testing",[18,86,87],{},"Test your actual application as a real user:",[36,89,90,94],{"number":38},[40,91,93],{"id":92},"open-devtools-network-tab","Open DevTools Network tab",[18,95,96],{},"Open your app in a browser, sign in as a test user, and open DevTools (F12) → Network tab.",[36,98,99,103],{"number":57},[40,100,102],{"id":101},"observe-supabase-requests","Observe Supabase requests",[18,104,105],{},"Filter for requests to your Supabase URL. Look at the response data to verify only authorized data is returned.",[36,107,108,112,115],{"number":70},[40,109,111],{"id":110},"try-to-manipulate-requests","Try to manipulate requests",[18,113,114],{},"In the Console, try to fetch data you shouldn't have access to:",[45,116,119],{"className":117,"code":118,"language":50},[48],"// Try to fetch another user's data\nconst { data, error } = await supabase\n  .from('todos')\n  .select('*')\n  .eq('user_id', 'another-user-uuid');\n\nconsole.log(data);  // Should be empty\nconsole.log(error); // Should show RLS error or empty result\n",[52,120,118],{"__ignoreMap":54},[28,122,124],{"id":123},"method-3-automated-testing","Method 3: Automated Testing",[18,126,127],{},"Write tests that verify RLS behavior:",[45,129,132],{"className":130,"code":131,"language":50},[48],"// rls.test.ts\nimport { createClient } from '@supabase/supabase-js';\n\ndescribe('RLS Policies', () => {\n  const supabase = createClient(\n    process.env.SUPABASE_URL!,\n    process.env.SUPABASE_ANON_KEY!\n  );\n\n  let userAClient: ReturnType\u003Ctypeof createClient>;\n  let userBClient: ReturnType\u003Ctypeof createClient>;\n\n  beforeAll(async () => {\n    // Sign in as User A\n    const { data: sessionA } = await supabase.auth.signInWithPassword({\n      email: 'user-a@test.com',\n      password: 'test-password'\n    });\n    userAClient = createClient(\n      process.env.SUPABASE_URL!,\n      process.env.SUPABASE_ANON_KEY!,\n      { global: { headers: { Authorization: `Bearer ${sessionA.session?.access_token}` } } }\n    );\n\n    // Sign in as User B\n    const { data: sessionB } = await supabase.auth.signInWithPassword({\n      email: 'user-b@test.com',\n      password: 'test-password'\n    });\n    userBClient = createClient(\n      process.env.SUPABASE_URL!,\n      process.env.SUPABASE_ANON_KEY!,\n      { global: { headers: { Authorization: `Bearer ${sessionB.session?.access_token}` } } }\n    );\n  });\n\n  test('User A cannot see User B data', async () => {\n    const { data } = await userAClient\n      .from('todos')\n      .select('*')\n      .eq('user_id', 'user-b-uuid');\n\n    expect(data).toHaveLength(0);\n  });\n\n  test('User A can only see own data', async () => {\n    const { data } = await userAClient\n      .from('todos')\n      .select('*');\n\n    // All returned rows should belong to User A\n    data?.forEach(row => {\n      expect(row.user_id).toBe('user-a-uuid');\n    });\n  });\n\n  test('User A cannot update User B data', async () => {\n    const { data, error } = await userAClient\n      .from('todos')\n      .update({ completed: true })\n      .eq('user_id', 'user-b-uuid')\n      .select();\n\n    expect(data).toHaveLength(0);\n  });\n});\n",[52,133,131],{"__ignoreMap":54},[28,135,137],{"id":136},"rls-testing-checklist","RLS Testing Checklist",[18,139,140],{},"For each table with RLS, verify:",[142,143,144,152,158,164,170,176],"ul",{},[145,146,147,151],"li",{},[148,149,150],"strong",{},"SELECT:"," Users can only see rows they should access",[145,153,154,157],{},[148,155,156],{},"INSERT:"," Users can only create rows with their own user_id",[145,159,160,163],{},[148,161,162],{},"UPDATE:"," Users can only modify their own rows",[145,165,166,169],{},[148,167,168],{},"DELETE:"," Users can only delete their own rows",[145,171,172,175],{},[148,173,174],{},"Anonymous:"," Unauthenticated users see only public data",[145,177,178,181],{},[148,179,180],{},"Cross-user:"," Users cannot access other users' data by guessing IDs",[183,184,185,188],"warning-box",{},[18,186,187],{},"Test Negative Cases",[18,189,190],{},"The most important tests are the ones that should fail. Always verify that users CANNOT access data they shouldn't. A passing SELECT test doesn't mean much if unauthorized users can also SELECT the same data.",[28,192,194],{"id":193},"common-testing-mistakes","Common Testing Mistakes",[142,196,197,203,209,215],{},[145,198,199,202],{},[148,200,201],{},"Testing with service_role key:"," The service_role key bypasses RLS. Always test with the anon key and authenticated users.",[145,204,205,208],{},[148,206,207],{},"Only testing happy paths:"," Make sure to test unauthorized access attempts.",[145,210,211,214],{},[148,212,213],{},"Forgetting INSERT policies:"," Users might be able to insert data claiming to be another user.",[145,216,217,220],{},[148,218,219],{},"Not testing after policy changes:"," Re-run tests whenever you modify RLS policies.",[222,223,224],"tip-box",{},[18,225,226],{},"Use CheckYourVibe:\nRun a scan\nto automatically detect missing or misconfigured RLS policies in your Supabase project.",[228,229,230,237,247],"faq-section",{},[231,232,234],"faq-item",{"question":233},"Why can I see all data in the Supabase dashboard?",[18,235,236],{},"The Supabase dashboard uses the service_role key, which bypasses RLS. This is by design so you can manage all data. Your app uses the anon key with RLS applied.",[231,238,240],{"question":239},"How do I test as an anonymous user?",[18,241,242,243,246],{},"In the SQL Editor, don't set ",[52,244,245],{},"request.jwt.claims.sub",". Or create a Supabase client without signing in. This simulates an unauthenticated user with the anon role.",[231,248,250],{"question":249},"What if my RLS test fails unexpectedly?",[18,251,252],{},"Check: 1) RLS is enabled on the table, 2) You have the correct policy for the operation, 3) The policy condition uses the right column name, 4) You're testing with the right user context.",[18,254,255,258,263,264,263,268],{},[148,256,257],{},"Related guides:",[259,260,262],"a",{"href":261},"/blog/how-to/setup-supabase-rls","How to Set Up Supabase RLS"," ·\n",[259,265,267],{"href":266},"/blog/how-to/supabase-rls-policies","How to Write RLS Policies",[259,269,271],{"href":270},"/blog/guides/supabase","Supabase Security Guide",[273,274,275,281,286],"related-articles",{},[276,277],"related-card",{"description":278,"href":279,"title":280},"Methods to verify your API keys aren't exposed in your codebase, git history, browser bundle, or network requests. Find ","/blog/how-to/check-exposed-keys","How to Check for Exposed API Keys",[276,282],{"description":283,"href":284,"title":285},"Complete guide to securing Clerk authentication. Set up middleware, protect routes, verify webhooks, manage users secure","/blog/how-to/clerk-security","How to Secure Clerk Authentication",[276,287],{"description":288,"href":289,"title":290},"Step-by-step guide to database connection pooling. Improve performance and security with PgBouncer, Prisma, and serverle","/blog/how-to/connection-pooling","How to Set Up Database Connection Pooling",{"title":54,"searchDepth":292,"depth":292,"links":293},2,[294,300,305,306,307],{"id":30,"depth":292,"text":31,"children":295},[296,298,299],{"id":42,"depth":297,"text":43},3,{"id":60,"depth":297,"text":61},{"id":73,"depth":297,"text":74},{"id":83,"depth":292,"text":84,"children":301},[302,303,304],{"id":92,"depth":297,"text":93},{"id":101,"depth":297,"text":102},{"id":110,"depth":297,"text":111},{"id":123,"depth":292,"text":124},{"id":136,"depth":292,"text":137},{"id":193,"depth":292,"text":194},"how-to","2026-01-27","Verify your Row Level Security policies work correctly. Learn SQL testing methods, browser testing, and automated testing strategies for Supabase RLS.",false,"md",null,"yellow",{},true,"Verify your Row Level Security policies work correctly.","/blog/how-to/test-supabase-rls","[object Object]","HowTo",{"title":5,"description":310},{"loc":318},"blog/how-to/test-supabase-rls",[],"summary_large_image","o4oMQWMGBHJjAoPGi61Law3O6N8DJ-wWucNYCbnEmOk",1775843927153]