🎁 Get the FREE AI Skills Starter Guide β€” Subscribe β†’
BytesAgainBytesAgain
πŸ¦€ ClawHub

Supabase Hakke

by @studio-hakke

Supabase integration for Hakke Studio projects. Auth, database, storage, edge functions. Use with vercel skill for full-stack deployment.

Versionv1.1.0
Downloads780
Installs2
TERMINAL
clawhub install supabase-hakke

πŸ“– About This Skill


name: supabase-hakke description: Supabase integration for Hakke Studio projects. Auth, database, storage, edge functions. Use with vercel skill for full-stack deployment. metadata: version: "1.1.0" author: "Bastian Berrios (SAURON)" requires: ["vercel"] tools: ["exec"] philosophy: "Type-safe, RLS-first, production-ready" tags: ["hakke", "supabase", "nextjs", "saas", "multi-tenant"]

Supabase for Hakke Studio

Supabase integration especΓ­fica para proyectos de Hakke Studio.

Proyectos Actuales

| Proyecto | Supabase URL | Uso | |----------|--------------|-----| | hakke-app | https://[project].supabase.co | SaaS multi-tenant |

CLI Installation

# Supabase CLI (ya instalado)
supabase --version

Si no estΓ‘ instalado

npm install -g supabase

Authentication

Login to Supabase

supabase login

This opens browser for OAuth login with contacto@hakke.cl.

Link Project

cd /home/bastianberrios/proyectos/HAKKE/hakke-app
supabase link --project-ref 


Database Operations

Generate Types

supabase gen types typescript --local > lib/supabase/database.types.ts

Create Migration

supabase migration new 

Apply Migration

supabase db push

Reset Database (DEV ONLY)

supabase db reset


Auth Operations

Get API Keys

supabase status

Returns:

  • anon key - Public key (client-side)
  • service_role key - Secret key (server-side ONLY)
  • Create User (Admin)

    -- In Supabase Dashboard SQL Editor
    INSERT INTO auth.users (email, encrypted_password, email_confirmed_at)
    VALUES (
      'user@example.com',
      crypt('password123', gen_salt('bf')),
      NOW()
    );
    


    RLS (Row Level Security)

    Enable RLS

    ALTER TABLE users ENABLE ROW LEVEL SECURITY;
    

    Create Policy

    -- Users can only see their own data
    CREATE POLICY "Users can view own data"
    ON users
    FOR SELECT
    USING (auth.uid() = id);

    -- Users can update own data CREATE POLICY "Users can update own data" ON users FOR UPDATE USING (auth.uid() = id);


    Storage Operations

    Create Bucket

    -- In Supabase Dashboard SQL Editor
    INSERT INTO storage.buckets (id, name, public)
    VALUES ('avatars', 'avatars', false);
    

    Storage Policy

    -- Users can upload to own folder
    CREATE POLICY "Users can upload avatars"
    ON storage.objects
    FOR INSERT
    WITH CHECK (
      bucket_id = 'avatars'
      AND auth.uid()::text = (storage.foldername(name))[1]
    );
    


    Edge Functions

    Create Function

    supabase functions new 
    

    Deploy Function

    supabase functions deploy 
    

    Invoke Function

    curl -i -L --request POST 'https://.supabase.co/functions/v1/' \
      --header 'Authorization: Bearer ' \
      --header 'Content-Type: application/json' \
      --data '{"name":"Functions"}'
    


    Multi-Tenant SaaS (hakke-app)

    Architecture

    β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”
    β”‚           hakke-app (SaaS)              β”‚
    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
    β”‚  tenants table (multi-tenant)           β”‚
    β”‚  - id, slug, name, plan                 β”‚
    β”‚  - owner_id (FK β†’ auth.users)           β”‚
    β”‚  - subscription_status                  β”‚
    β”œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€
    β”‚  Row Level Security (RLS)               β”‚
    β”‚  - tenant_id = current_tenant()         β”‚
    β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
    

    Tenant Isolation

    -- Function to get current tenant
    CREATE OR REPLACE FUNCTION current_tenant_id()
    RETURNS uuid AS $$
    BEGIN
      RETURN (auth.jwt()->>'tenant_id')::uuid;
    END;
    $$ LANGUAGE plpgsql SECURITY DEFINER;

    -- RLS Policy example CREATE POLICY "Users can only see own tenant data" ON appointments FOR ALL USING (tenant_id = current_tenant_id());

    Subscription Management

    -- Check subscription status
    CREATE OR REPLACE FUNCTION has_active_subscription()
    RETURNS boolean AS $$
    DECLARE
      tenant_record tenants%ROWTYPE;
    BEGIN
      SELECT * INTO tenant_record
      FROM tenants
      WHERE id = current_tenant_id();
      
      RETURN tenant_record.subscription_status = 'active';
    END;
    $$ LANGUAGE plpgsql SECURITY DEFINER;
    


    Common Queries

    List Tables

    SELECT table_name
    FROM information_schema.tables
    WHERE table_schema = 'public';
    

    Describe Table

    SELECT
      column_name,
      data_type,
      is_nullable,
      column_default
    FROM information_schema.columns
    WHERE table_name = 'users';
    

    Check RLS

    SELECT
      schemaname,
      tablename,
      rowsecurity
    FROM pg_tables
    WHERE schemaname = 'public';
    


    Environment Variables

    .env.local

    NEXT_PUBLIC_SUPABASE_URL=https://.supabase.co
    NEXT_PUBLIC_SUPABASE_ANON_KEY=
    SUPABASE_SERVICE_ROLE_KEY=
    

    .env.example (commit to git)

    NEXT_PUBLIC_SUPABASE_URL=
    NEXT_PUBLIC_SUPABASE_ANON_KEY=
    SUPABASE_SERVICE_ROLE_KEY=
    


    Client Setup

    Server Client (Next.js)

    // lib/supabase/server.ts
    import { createClient } from '@supabase/supabase-js';
    import { cookies } from 'next/headers';

    export function createServerClient() { const cookieStore = cookies(); return createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!, { auth: { persistSession: false, }, } ); }

    Browser Client (Next.js)

    // lib/supabase/client.ts
    import { createClient } from '@supabase/supabase-js';

    export const supabase = createClient( process.env.NEXT_PUBLIC_SUPABASE_URL!, process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY! );


    Troubleshooting

    Connection Issues

    # Check if Supabase is accessible
    curl -I https://.supabase.co/rest/v1/

    Expected: 401 Unauthorized (means API is working, just need auth)

    RLS Not Working

    -- Check if RLS is enabled
    SELECT tablename, rowsecurity
    FROM pg_tables
    WHERE schemaname = 'public';

    -- Check policies SELECT schemaname, tablename, policyname, permissive, roles, cmd, qual, with_check FROM pg_policies WHERE schemaname = 'public';

    Auth Issues

    # Check if user exists
    supabase auth list

    Check logs

    supabase logs auth


    Integration with Vercel

    Deploy with Vercel

    # In hakke-app directory
    vercel

    Add env vars in Vercel Dashboard or CLI

    vercel env add NEXT_PUBLIC_SUPABASE_URL vercel env add NEXT_PUBLIC_SUPABASE_ANON_KEY vercel env add SUPABASE_SERVICE_ROLE_KEY


    Best Practices

    | Practice | Why | |----------|-----| | RLS always on | Security first | | Service key server-only | Never expose to client | | Type generation | Type-safe queries | | Migrations in git | Reproducible DB | | Separate anon/service keys | Principle of least privilege |


    Resources

  • Docs: https://supabase.com/docs
  • Dashboard: https://supabase.com/dashboard
  • CLI Reference: https://supabase.com/docs/reference/cli
  • SQL Editor: Dashboard β†’ SQL Editor
  • Logs: Dashboard β†’ Logs

  • *Supabase Hakke v1.1.0 - Production-ready for Hakke Studio*

    πŸ“‹ Tips & Best Practices

    | Practice | Why | |----------|-----| | RLS always on | Security first | | Service key server-only | Never expose to client | | Type generation | Type-safe queries | | Migrations in git | Reproducible DB | | Separate anon/service keys | Principle of least privilege |