| name | supabase-development |
|---|---|
| description | Work with Supabase in the lexico project - migrations, RLS policies, Edge Functions, and type generation. Use this skill when modifying the lexico database or authentication. |
| license | MIT |
This skill covers working with Supabase in the lexico project, including database migrations, Row Level Security (RLS) policies, Edge Functions, and TypeScript type generation.
The lexico application uses Supabase for:
- PostgreSQL database with RLS policies
- Authentication via multiple OAuth providers
- Edge Functions for server-side logic
- Storage for user-generated content
For comprehensive architecture and patterns, see applications/lexico/AGENTS.md.
# Start local Supabase (Docker-based)
nx run lexico:supabase:start
# Stop local environment
nx run lexico:supabase:stop
# Reset database (destructive)
nx run lexico:supabase:resetThe local environment runs on:
- PostgreSQL: localhost:54322
- API: http://localhost:54321
- Studio: http://localhost:54323
After any schema changes, regenerate TypeScript types:
# Generate types from schema
nx run lexico:supabase:generate-typesThis creates/updates applications/lexico/src/lib/database.types.ts with:
- Table definitions
- RPC function signatures
- Enum types
- View definitions
IMPORTANT: Always regenerate types after schema changes or your build will fail type checking.
nx run lexico:supabase:database-diff
nx run lexico:supabase:generate-types
nx run lexico:test:integrationSee testing-strategy for test naming and target patterns.
# Make changes in Supabase Studio (http://localhost:54323)
# Then generate a migration file
nx run lexico:supabase:database-diff --name="add_bookmarks_table"This creates a new migration file in applications/lexico/supabase/migrations/ with the schema diff.
Migrations are timestamped SQL files:
supabase/migrations/
20240101120000_initial_schema.sql
20240115093000_add_bookmarks_table.sql
20240120154500_add_rls_policies.sql
Migrations are applied automatically when:
- Starting local environment (
nx run lexico:supabase:start) - Deploying to production (via Supabase CLI)
- One logical change per migration - easier to review and rollback
- Include both up and down migrations when possible
- Test locally first before deploying to production
- Use transactions for multi-statement migrations
- Add comments explaining complex changes
Example migration:
-- Add bookmarks table for user favorites
CREATE TABLE bookmarks (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
user_id uuid REFERENCES auth.users(id) ON DELETE CASCADE,
word_id text NOT NULL,
created_at timestamptz DEFAULT now()
);
-- Create index for faster lookups
CREATE INDEX bookmarks_user_id_idx ON bookmarks(user_id);
-- Enable RLS
ALTER TABLE bookmarks ENABLE ROW LEVEL SECURITY;
-- Users can only see their own bookmarks
CREATE POLICY "Users can view own bookmarks"
ON bookmarks FOR SELECT
USING (auth.uid() = user_id);The lexico database enforces RLS on all user-facing tables. Common patterns:
User-owned resources:
CREATE POLICY "Users can manage own bookmarks"
ON bookmarks FOR ALL
USING (auth.uid() = user_id)
WITH CHECK (auth.uid() = user_id);Public read, authenticated write:
CREATE POLICY "Public can read words"
ON words FOR SELECT
USING (true);
CREATE POLICY "Authenticated users can add words"
ON words FOR INSERT
TO authenticated
WITH CHECK (true);Admin-only access:
CREATE POLICY "Admins can manage users"
ON user_profiles FOR ALL
USING (auth.jwt() ->> 'role' = 'admin');Test policies in Supabase Studio SQL Editor with different user contexts:
-- Set user context
SET LOCAL role TO authenticated;
SET LOCAL "request.jwt.claims" TO '{"sub": "user-uuid-here"}';
-- Test query
SELECT * FROM bookmarks; -- Should only return user's bookmarkslexico supports multiple OAuth providers configured in Supabase:
- Google OAuth
- GitHub OAuth
- Twitter OAuth
Configuration is in Supabase project settings → Authentication → Providers.
Authentication uses cookie-based sessions for SSR compatibility:
- User authenticates via OAuth provider
- Supabase returns session tokens
- TanStack Start server sets secure HTTP-only cookies
- Subsequent requests include cookies for server-side auth
- RLS policies enforce data access based on
auth.uid()
See applications/lexico/AGENTS.md for detailed authentication flow diagrams.
import { createBrowserClient } from "@/lib/supabase.client";
const supabase = createBrowserClient();
// Sign in
const { data, error } = await supabase.auth.signInWithOAuth({
provider: "google",
options: {
redirectTo: `${window.location.origin}/auth/callback`,
},
});
// Sign out
await supabase.auth.signOut();import { createServerClient } from "@/lib/supabase.server";
export const getUser = async () => {
const supabase = await createServerClient();
const {
data: { user },
error,
} = await supabase.auth.getUser();
return user;
};Edge Functions run on Supabase's global network. Use for:
- Webhooks
- Scheduled tasks
- Complex server-side logic
- External API integrations
# Create new Edge Function
supabase functions new my-function
# Serve locally
supabase functions serve my-function
# Deploy to production
supabase functions deploy my-functionEdge Functions are TypeScript files in supabase/functions/:
supabase/functions/
my-function/
index.ts
import { serve } from "https://deno.land/std@0.168.0/http/server.ts";
import { createClient } from "https://esm.sh/@supabase/supabase-js@2";
serve(async (req) => {
const supabase = createClient(
Deno.env.get("SUPABASE_URL") ?? "",
Deno.env.get("SUPABASE_ANON_KEY") ?? "",
);
// Get user from Authorization header
const authHeader = req.headers.get("Authorization")!;
const token = authHeader.replace("Bearer ", "");
const {
data: { user },
} = await supabase.auth.getUser(token);
if (!user) {
return new Response("Unauthorized", { status: 401 });
}
// Function logic here
return new Response(JSON.stringify({ success: true }), {
headers: { "Content-Type": "application/json" },
});
});The lexico database includes:
- users: User profiles and preferences
- bookmarks: User-saved words
- word_library: Custom word collections
- search_history: User search tracking
See database migrations in applications/lexico/supabase/migrations/ for complete schema.
Stored procedures for complex queries:
CREATE OR REPLACE FUNCTION search_words(
query text,
limit_count int DEFAULT 20
)
RETURNS TABLE (word_id text, rank numeric)
LANGUAGE plpgsql
AS $$
BEGIN
RETURN QUERY
SELECT id, ts_rank(search_vector, plainto_tsquery('latin', query))
FROM words
WHERE search_vector @@ plainto_tsquery('latin', query)
ORDER BY rank DESC
LIMIT limit_count;
END;
$$;Call from TypeScript:
const { data, error } = await supabase.rpc("search_words", {
query: "amor",
limit_count: 20,
});Supabase local environment uses these variables (auto-configured):
SUPABASE_URL=http://localhost:54321
SUPABASE_ANON_KEY=<local-anon-key>
SUPABASE_SERVICE_ROLE_KEY=<local-service-key>Production credentials are in Supabase project settings:
SUPABASE_URL=https://<project-id>.supabase.co
SUPABASE_ANON_KEY=<production-anon-key>
SUPABASE_SERVICE_ROLE_KEY=<production-service-key>NEVER commit service role keys to version control.
Create seed data in supabase/seed.sql:
-- Insert test data
INSERT INTO words (id, latin, english) VALUES
('amor', 'amor', 'love'),
('vita', 'vita', 'life');Apply seeds:
nx run lexico:supabase:reset # Includes seeding# Export schema and data
supabase db dump -f backup.sql
# Schema only
supabase db dump --schema-only -f schema.sqlEnable RLS logging in SQL:
ALTER DATABASE postgres SET log_statement = 'all';View logs in Supabase Studio → Logs → Database.
Type generation fails:
- Ensure local Supabase is running
- Check for SQL syntax errors in migrations
- Verify all tables have primary keys
RLS policy blocks queries:
- Check
auth.uid()in SQL editor - Verify user is authenticated
- Review policy USING/WITH CHECK clauses
Migration conflicts:
- Reset local database:
nx run lexico:supabase:reset - Check for duplicate migration timestamps
- Ensure migrations are idempotent
- applications/lexico/AGENTS.md - Lexico architecture
- applications/lexico/README.md - Getting started
- Supabase Docs - Official documentation
- Always enable RLS on user-facing tables
- Test policies thoroughly with different user contexts
- Use TypeScript types for type safety
- Regenerate types after every schema change
- Write migrations incrementally for easier review
- Use transactions for multi-statement operations
- Index foreign keys for better query performance
- Document RLS policies with SQL comments