Source profileQuality 92/100

agents-inc/skills/src/skills/api-baas-supabase/SKILL.md

api-baas-supabase

Supabase backend-as-a-service — Auth, Database, Realtime, Storage, Edge Functions, RLS policies, typed client

Source repository stars
23
Declared platforms
0
Static risk flags
0
Last source update
2026-08-09
Source checked
2026-08-28

Decision brief

What it does: where it fits

Quick Guide: Use Supabase as your backend-as-a-service for Postgres database, authentication, realtime subscriptions, file storage, and edge functions. Always use the typed client with Database generic, enable RLS on every table, and use the secret key only on the server.

Best for

    Not for

    • Tasks that require unconfirmed production actions or broad system permissions.
    • Environments where the pinned source and install steps cannot be inspected.

    Compatibility matrix

    Platform support, with evidence labels

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    Installation

    Inspect first. Install second.

    The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.

    Source-detected install commandSource
    npx skills add https://github.com/agents-inc/skills --skill "src/skills/api-baas-supabase"
    Safe inspection promptEditorial

    Inspect the Agent Skill "api-baas-supabase" from https://github.com/agents-inc/skills/blob/81d43a51211aca12c85dcc16085fa99014ec548e/src/skills/api-baas-supabase/SKILL.md at commit 81d43a51211aca12c85dcc16085fa99014ec548e. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.

    Workflow

    What the source asks the agent to do

    1. 01

      Pattern 1: Typed Client Setup

      Always pass the Database generic to createClient for full autocomplete on table names, column names, and return types. Use environment variables for URL and keys.

      Always pass the Database generic to createClient for full autocomplete on table names, column names, and return types. Use environment variables for URL and keys.Without the generic, typos in table/column names are not caught at compile time. See examples/core.md for browser, server, and admin client setup patterns.
    2. 02

      CRITICAL: Before Using This Skill

      All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

      Setting up a Supabase client with TypeScript type safetyImplementing authentication (email/password, OAuth, magic links, session management)Querying Postgres via the Supabase client (select, insert, update, delete, RPC)
    3. 03

      Philosophy

      Supabase is an open-source Firebase alternative built on Postgres. It provides a complete backend through a combination of Postgres extensions, auto-generated REST/GraphQL APIs, authentication, realtime subscriptions, file storage, and edge functions.

      Postgres at the core — Every feature is built on Postgres. RLS policies, auth, and realtime all leverage Postgres primitives. Understanding Postgres is understanding Supabase.Type safety end-to-end — Generate TypeScript types from your database schema with supabase gen types. Pass the Database generic to createClient for fully typed queries.Security by default — RLS must be enabled on every table. The publishable key is safe for browsers (RLS enforces access). The secret key bypasses RLS and must never leave the server.
    4. 04

      Core Patterns

      Always pass the Database generic to createClient for full autocomplete on table names, column names, and return types. Use environment variables for URL and keys.

      Always pass the Database generic to createClient for full autocomplete on table names, column names, and return types. Use environment variables for URL and keys.Without the generic, typos in table/column names are not caught at compile time. See examples/core.md for browser, server, and admin client setup patterns.Every Supabase method returns { data, error }. Always destructure and check error before using data. Never use non-null assertions on data.
    5. 05

      Pattern 2: Error Handling with { data, error }

      Every Supabase method returns { data, error }. Always destructure and check error before using data. Never use non-null assertions on data.

      Every Supabase method returns { data, error }. Always destructure and check error before using data. Never use non-null assertions on data.See examples/core.md for the reusable error handler pattern and common mistakes.

    Permission review

    Static risk signals and limitations

    No configured static risk pattern was detected

    This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.

    Evidence record

    Why each signal appears

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score92/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars23SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated or reviewed according to the visible evidence level

    Pinned source

    Provenance and original SKILL.md

    Repository
    agents-inc/skills
    Skill path
    src/skills/api-baas-supabase/SKILL.md
    Commit
    81d43a51211aca12c85dcc16085fa99014ec548e
    License
    MIT
    Collected
    2026-08-28
    Default branch
    main
    View the original SKILL.md

    Supabase Patterns

    Quick Guide: Use Supabase as your backend-as-a-service for Postgres database, authentication, realtime subscriptions, file storage, and edge functions. Always use the typed client with Database generic, enable RLS on every table, and use the secret key only on the server.


    <critical_requirements>

    CRITICAL: Before Using This Skill

    All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

    (You MUST enable Row Level Security (RLS) on EVERY table in an exposed schema — no exceptions)

    (You MUST use the Database generic type with createClient<Database>() for type-safe queries)

    (You MUST NEVER expose the secret key in client-side code — use the publishable key in browsers, the secret key only on the server)

    (You MUST use (select auth.uid()) wrapped in a subquery inside RLS policies for performance)

    (You MUST handle all Supabase responses with { data, error } destructuring — never assume success)

    </critical_requirements>


    Auto-detection: Supabase, createClient, @supabase/supabase-js, @supabase/ssr, supabase-js, auth.uid(), RLS, row level security, realtime, postgres_changes, supabase.auth, supabase.from, supabase.storage, supabase.functions, supabase.channel, edge function, Deno.serve

    When to use:

    • Setting up a Supabase client with TypeScript type safety
    • Implementing authentication (email/password, OAuth, magic links, session management)
    • Querying Postgres via the Supabase client (select, insert, update, delete, RPC)
    • Writing Row Level Security policies for data access control
    • Subscribing to database changes in real time
    • Uploading and serving files from Supabase Storage
    • Building serverless functions with Supabase Edge Functions (Deno)

    Key patterns covered:

    • Typed client setup with Database generic and environment variables
    • Auth flows: sign up, sign in, OAuth, magic link, session refresh, onAuthStateChange
    • Database queries with filters, joins, RPC calls, and error handling
    • RLS policies: USING vs WITH CHECK, auth.uid(), role-based access
    • Realtime subscriptions via channel().on('postgres_changes')
    • Storage: upload, signed URLs, public URLs, bucket policies
    • Edge Functions: Deno.serve, CORS headers, secrets, Supabase client in functions

    When NOT to use:

    • Direct Postgres connections (use a database driver skill instead)
    • Complex server-side ORM patterns (use a dedicated ORM skill)
    • Non-Supabase authentication providers (use dedicated auth skills)

    Detailed Resources:

    • For decision frameworks and anti-patterns, see reference.md

    Client & Queries:

    Authentication:

    • examples/auth.md — Full auth flows, OAuth, magic links, session refresh, middleware protection

    Database:

    Storage:

    Edge Functions:


    Philosophy

    Supabase is an open-source Firebase alternative built on Postgres. It provides a complete backend through a combination of Postgres extensions, auto-generated REST/GraphQL APIs, authentication, realtime subscriptions, file storage, and edge functions.

    Core principles:

    1. Postgres at the core — Every feature is built on Postgres. RLS policies, auth, and realtime all leverage Postgres primitives. Understanding Postgres is understanding Supabase.
    2. Type safety end-to-end — Generate TypeScript types from your database schema with supabase gen types. Pass the Database generic to createClient for fully typed queries.
    3. Security by default — RLS must be enabled on every table. The publishable key is safe for browsers (RLS enforces access). The secret key bypasses RLS and must never leave the server.
    4. Error as values — Every Supabase method returns { data, error }. Never assume success. Always check error before using data.
    5. Realtime built in — Postgres changes stream over WebSockets via channels. No separate pub/sub infrastructure needed.
    6. Edge-first functions — Edge Functions run Deno at the edge, close to users. Design for short-lived, idempotent operations.

    When to use Supabase:

    • Rapid backend development with Postgres, auth, and storage out of the box
    • Projects needing realtime features (chat, notifications, live dashboards)
    • Teams wanting to avoid managing separate auth, database, and storage services
    • Applications that benefit from Row Level Security for multi-tenant data isolation

    When NOT to use:

    • Complex server-side business logic requiring a full application server (use Edge Functions for simple cases, a dedicated API for complex ones)
    • Applications needing an ORM with advanced query building (Supabase query builder is powerful but not a full ORM)
    • Offline-first applications requiring complex sync protocols

    Core Patterns

    Pattern 1: Typed Client Setup

    Always pass the Database generic to createClient for full autocomplete on table names, column names, and return types. Use environment variables for URL and keys.

    export const supabase = createClient<Database>(
      SUPABASE_URL,
      SUPABASE_PUBLISHABLE_KEY,
    );
    

    Without the generic, typos in table/column names are not caught at compile time. See examples/core.md for browser, server, and admin client setup patterns.


    Pattern 2: Error Handling with { data, error }

    Every Supabase method returns { data, error }. Always destructure and check error before using data. Never use non-null assertions on data.

    const { data, error } = await supabase
      .from("profiles")
      .select("id, username")
      .eq("id", userId)
      .single();
    if (error) throw new Error(`Failed to fetch profile: ${error.message}`);
    

    See examples/core.md for the reusable error handler pattern and common mistakes.


    Pattern 3: Authentication Flows

    Supabase Auth supports email/password (signInWithPassword), OAuth (signInWithOAuth), magic links (signInWithOtp), and phone OTP. Register onAuthStateChange early in the app lifecycle and always clean up with subscription.unsubscribe().

    Key gotcha: Do NOT call Supabase methods directly inside onAuthStateChange — use setTimeout(..., 0) to defer.

    See examples/auth.md for sign up, sign in, OAuth, magic link, session management, middleware protection, and password reset patterns.


    Pattern 4: Database Queries

    Use the query builder for type-safe CRUD with filters, joins, ordering, and pagination. Always add .select() after .insert() or .update() to return the affected row.

    const { data, error } = await supabase
      .from("posts")
      .select("id, title, author:profiles(username)")
      .eq("published", true)
      .order("created_at", { ascending: false })
      .range(0, PAGE_SIZE - 1);
    

    See examples/database.md for complex queries, upserts, RPC calls, conditional filters, counting, and migrations.


    Pattern 5: Row Level Security (RLS) Policies

    RLS is the primary security mechanism. Enable it on every table, write separate policies per operation (not FOR ALL), and wrap auth.uid() in a subquery for performance.

    alter table public.posts enable row level security;
    
    create policy "posts_select" on public.posts for select to authenticated
    using ( published = true or (select auth.uid()) = author_id );
    

    Never trust user_metadata from JWT for access control — it is user-modifiable. See examples/database.md for full CRUD policies, team-based access, and anti-patterns.


    Pattern 6: Realtime Subscriptions

    Subscribe to database changes via channel().on('postgres_changes', ...). Always unsubscribe on cleanup. DELETE events cannot be filtered — all deletes are received. UPDATE/DELETE payloads need replica identity full for old record data.

    const channel = supabase
      .channel("room-messages")
      .on(
        "postgres_changes",
        {
          event: "INSERT",
          schema: "public",
          table: "messages",
          filter: `room_id=eq.${roomId}`,
        },
        (payload) => {
          /* handle */
        },
      )
      .subscribe();
    

    Use for chat, live dashboards, notifications. Avoid for high-frequency data (> 100 updates/sec).


    Pattern 7: Storage Operations

    Upload files with supabase.storage.from(bucket).upload(). Use getPublicUrl() for public buckets, createSignedUrl() for private buckets with time-limited access. Storage access control uses RLS on storage.objects.

    See examples/storage.md for upload, signed URLs, public URLs, image transforms, bucket policies, and signed upload URLs.


    Pattern 8: Edge Functions

    Use Deno.serve() (not the deprecated serve import). Import supabase-js with npm: prefix: import { createClient } from "npm:@supabase/supabase-js@2". Handle CORS on every response. Use Deno.env.get() for secrets. Forward user JWT for RLS enforcement.

    See examples/edge-functions.md for basic functions, authenticated access, shared utilities, webhooks, multi-route "fat functions", and background processing with EdgeRuntime.waitUntil().


    <decision_framework>

    Decision Framework

    Which Supabase Key to Use

    Where is the code running?
    ├─ Browser / Client-side → publishable key (RLS enforced)
    ├─ Server / API route → publishable key + user JWT (RLS enforced per user)
    └─ Admin / Migration script → secret key (bypasses RLS)
        └─ NEVER expose the secret key in client bundles
    

    Auth Method Selection

    What auth flow does the user need?
    ├─ Email + Password → signInWithPassword
    ├─ Social login (GitHub, Google, etc.) → signInWithOAuth
    ├─ Passwordless email → signInWithOtp (magic link)
    ├─ Phone + SMS → signInWithOtp (phone)
    └─ SSO / SAML → signInWithSSO (enterprise)
    

    Realtime vs Polling

    How fresh must the data be?
    ├─ Instant (< 1 second) → Realtime subscription (postgres_changes)
    ├─ Near-instant (1-5 seconds) → Realtime subscription
    ├─ Periodic (> 5 seconds ok) → Polling with setInterval
    └─ On-demand (user refresh) → Re-fetch on action
        └─ High-frequency updates (> 100/sec)?
            ├─ YES → Polling or batch (Realtime has per-subscriber checks)
            └─ NO → Realtime is fine
    

    Storage: Public vs Private Buckets

    Who should access the files?
    ├─ Anyone (public assets, avatars) → Public bucket + getPublicUrl()
    ├─ Authenticated users only → Private bucket + createSignedUrl()
    ├─ Specific users (own files) → Private bucket + RLS on storage.objects
    └─ Server-only processing → secret key for upload/download
    

    Edge Functions vs Client Queries

    Does the operation need server-side logic?
    ├─ Simple CRUD → Client query with RLS (no edge function needed)
    ├─ Multi-step / transactional → Edge function or Postgres function (RPC)
    ├─ Third-party API call → Edge function
    ├─ Webhook receiver → Edge function
    └─ Heavy computation → Edge function with EdgeRuntime.waitUntil() for background work
    

    </decision_framework>


    <red_flags>

    RED FLAGS

    High Priority Issues:

    • Missing RLS on tables — Any table without RLS in an exposed schema is completely open to the public. In January 2025, 170+ apps were found with exposed databases due to missing RLS (CVE-2025-48757).
    • Secret key in client code — The secret key (formerly service_role key) bypasses all RLS. Exposing it in browser bundles gives every user full admin database access.
    • Ignoring { data, error } returns — Accessing data without checking error leads to runtime crashes when operations fail.
    • Using auth.jwt() ->> 'user_metadata' in RLS policiesuser_metadata is modifiable by authenticated users via updateUser(). Never use it for access control decisions.

    Medium Priority Issues:

    • Using FOR ALL in RLS policies — Separate into SELECT, INSERT, UPDATE, DELETE policies for clarity and auditability.
    • Bare auth.uid() in policies without subquery — Wrap in (select auth.uid()) for up to 94-99% performance improvement per Supabase benchmarks.
    • Not specifying to authenticated or to anon in policies — Without a role, policies apply to all roles, which may expose data unintentionally.
    • Using select("*") everywhere — Fetches all columns including sensitive data. Select only the columns you need.
    • Deprecated serve import in Edge Functionsimport { serve } from "https://deno.land/std/http/server.ts" is deprecated. Use Deno.serve().

    Common Mistakes:

    • Not adding .select() after .insert() or .update() — Without .select(), these methods return no data (only null).
    • Missing CORS headers in Edge Functions — Browser requests fail without proper CORS headers and OPTIONS handling.
    • Not unsubscribing from Realtime channels — Leaks WebSocket connections and can cause memory issues.
    • Using bare specifiers in Edge Functionsimport { createClient } from "@supabase/supabase-js" fails in Deno. Use npm:@supabase/supabase-js@2.
    • Using getSession() to verify authgetSession() reads from local storage and can be tampered with. Use getUser() for secure server-side verification.

    Gotchas & Edge Cases:

    • Realtime DELETE events cannot be filtered — All deletes for a subscribed table are received regardless of filter.
    • Realtime requires replica identity full for old record data — By default, UPDATE and DELETE payloads only include the new record. Set alter table X replica identity full to access payload.old.
    • RLS policies are not applied to Realtime DELETE events — Be cautious about what information DELETE events expose.
    • onAuthStateChange fires on tab focusSIGNED_IN events fire when a browser tab regains focus, not just on actual sign-in.
    • Do NOT call Supabase methods inside onAuthStateChange callback — This can cause deadlocks. Use setTimeout(..., 0) to defer.
    • Signed URLs expirecreateSignedUrl() URLs expire after the specified duration. Signed upload URLs expire after 2 hours.
    • Public bucket URLs bypass RLS — Files in public buckets are accessible to anyone with the URL, regardless of policies.
    • Edge Function cold starts — First invocation after idle period has additional latency. Design "fat functions" (fewer, larger functions) to minimize cold starts.
    • Edge Functions: file writes only on /tmp — The /tmp directory is the only writable path in edge functions.

    </red_flags>


    <critical_reminders>

    CRITICAL REMINDERS

    All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering, import type, named constants)

    (You MUST enable Row Level Security (RLS) on EVERY table in an exposed schema — no exceptions)

    (You MUST use the Database generic type with createClient<Database>() for type-safe queries)

    (You MUST NEVER expose the secret key in client-side code — use the publishable key in browsers, the secret key only on the server)

    (You MUST use (select auth.uid()) wrapped in a subquery inside RLS policies for performance)

    (You MUST handle all Supabase responses with { data, error } destructuring — never assume success)

    Failure to follow these rules will create security vulnerabilities, type-unsafe queries, and silent runtime failures.

    </critical_reminders>

    Frequently asked questions

    What to verify before installation and use

    What does the api-baas-supabase source document cover?

    Quick Guide: Use Supabase as your backend-as-a-service for Postgres database, authentication, realtime subscriptions, file storage, and edge functions. Always use the typed client with Database generic, enable RLS on every table, and use the secret key only on the server.

    How do I install api-baas-supabase?

    The source record exposes this install command: npx skills add https://github.com/agents-inc/skills --skill "src/skills/api-baas-supabase". Inspect the command and pinned source before running it.

    Alternatives

    Compare before choosing

    Computed 10045,960

    coreyhaines31/marketingskills

    ab-testing

    When the user wants to plan, design, or implement an A/B test or experiment, or build a growth experimentation program. Also use when the user mentions "A/B test," "split test," "experiment," "test this change," "variant copy," "multivariate test," "hypothesis," "should I test this," "which version is better," "test two versions," "statistical significance," "how long should I run this test," "growth experiments," "experiment velocity," "experiment backlog," "ICE score," "experimentation program

    Computed 10029,236

    garrytan/gbrain

    bulk-ingestion

    End-to-end discipline for turning any large data source (audio libraries, email takeouts, document corpora, chat exports, API dumps) into brain pages at scale. The lifecycle spine: SCHEMA → ACCESS → TRIAL → EVALUATE → IMPROVE → CODIFY → TEST → SKILLIFY → BULK → MONITOR. State is tracked in a durable JSON manifest (see MANIFEST-PATTERN.md) so any crash, session boundary, or subagent fan-out resumes from ground truth instead of memory.

    Computed 10025,136

    alirezarezvani/claude-skills

    app-store-optimization

    App Store Optimization (ASO) toolkit for researching keywords, analyzing competitor rankings, generating metadata suggestions, and improving app visibility on Apple App Store and Google Play Store. Use when the user asks about ASO, app store rankings, app metadata, app titles and descriptions, app store listings, app visibility, or mobile app marketing on iOS or Android. Supports keyword research and scoring, competitor keyword analysis, metadata optimization, A/B test planning, launch checklist

    Computed 1005,277

    dotnet/skills

    migrate-vstest-to-mtp

    Migrates .NET test projects from VSTest to Microsoft.Testing.Platform (MTP). Use when user asks to "migrate to MTP", "switch from VSTest", "enable Microsoft.Testing.Platform", "use MTP runner", set OutputType=Exe only for test projects in Directory.Build.props, or mentions EnableMSTestRunner, EnableNUnitRunner, or UseMicrosoftTestingPlatformRunner. USE FOR: MTP behavioral differences vs VSTest (exit code 8, zero tests discovered, --ignore-exit-code, TESTINGPLATFORM_EXITCODE_IGNORE); centralizing