To secure a Vue + Supabase SPA, you need to: (1) enable RLS on all tables since Vue SPAs have no server layer, (2) use Pinia for reactive auth state management, (3) implement vue-router navigation guards for UX (not security), (4) use only the anon key in client code, and (5) test RLS policies thoroughly before launch. This blueprint covers Vue 3 Composition API patterns with Supabase.
TL;DR
Vue SPAs with Supabase rely entirely on RLS for data security. Key tasks: enable RLS on all tables, use Pinia for reactive auth state, add vue-router navigation guards for UX (not security), and remember that route guards are client-side only-RLS is your actual security layer.
RLS Configuration Supabase
-- Users can only access their own data
CREATE POLICY "User data isolation"
ON user_data FOR ALL
USING (auth.uid() = user_id);
-- Public read, authenticated write
CREATE POLICY "Public posts"
ON posts FOR SELECT
USING (true);
CREATE POLICY "Authenticated create"
ON posts FOR INSERT
WITH CHECK (auth.uid() = author_id);
Auth Store with Pinia Vue
import { defineStore } from 'pinia'
import { supabase } from '@/lib/supabase'
export const useAuthStore = defineStore('auth', {
state: () => ({
user: null as User | null,
loading: true,
}),
actions: {
async init() {
const { data } = await supabase.auth.getSession()
this.user = data.session?.user ?? null
this.loading = false
supabase.auth.onAuthStateChange((_event, session) => {
this.user = session?.user ?? null
})
},
},
})
Router Guards Vue
router.beforeEach(async (to, from, next) => {
const authStore = useAuthStore()
// Wait for auth to initialize
if (authStore.loading) {
await new Promise(resolve => {
const unwatch = watch(() => authStore.loading, (loading) => {
if (!loading) { unwatch(); resolve(true) }
})
})
}
if (to.meta.requiresAuth && !authStore.user) {
next('/login')
} else {
next()
}
})
Router guards are UX only. Any JavaScript can be disabled or bypassed. Your Supabase RLS policies are the only real security layer.
Security Checklist
Pre-Launch Checklist
RLS enabled on all tables
RLS policies tested thoroughly
Auth state managed with Pinia
Only anon key in client code
Environment variables configured
Alternative Stacks
Consider these related blueprints:
- Nuxt + Supabase - With server-side rendering
- Vue + Firebase - Firebase/Firestore alternative
- React + Supabase - React framework alternative