filipecabaco

nano-supabase

Skill for working with the nano-supabase codebase: a lightweight TypeScript library that emulates Supabase entirely in-browser/edge using PGlite (Postgres via WASM). Covers architecture, fetch adapter routing, auth/storage/data flows, PostgREST WASM parser, connection pooling, cross-runtime compatibility, build system, and testing conventions. Use when implementing features, fixing bugs, writing tests, or reviewing code in this project.

filipecabaco 7 1 Updated 3mo ago

Resources

15
GitHub

Install

npx skillscat add filipecabaco/nano-supabase

Install via the SkillsCat registry.

SKILL.md

nano-supabase

Lightweight Supabase emulation running entirely in-browser/edge. Zero network calls. Full auth (JWT + RLS), storage (pluggable backends), PostgREST query parsing (WASM), priority-queue connection pooling over PGlite.

Architecture

@supabase/supabase-js
  -> Fetch Adapter (routes by URL path)
     /auth/v1/*    -> AuthHandler (signup, signin, signout, refresh)
     /rest/v1/*    -> PostgREST Parser (WASM) -> SQL
     /storage/v1/* -> StorageHandler (buckets, objects, signed URLs)
  -> Priority Queue (CRITICAL / HIGH / MEDIUM / LOW)
  -> Connection Pooler (N-to-1 multiplexing)
  -> PGlite (PostgreSQL in WebAssembly)

Project Layout

src/
  index.ts              # Public exports
  client.ts             # createLocalSupabaseClient, createFetchAdapter
  supabase-client.ts    # Supabase-compatible client wrapper
  postgrest-parser.ts   # PostgREST -> SQL (WASM binding)
  pooler.ts             # N-to-1 connection pooler
  queue.ts              # Priority queue (4 levels)
  types.ts              # QueryPriority, QueryResult, PoolerConfig
  auth/                 # JWT via Web Crypto, sessions, refresh tokens
    handler.ts          # signUp, signIn, signOut, refreshToken
    crypto.ts           # Web Crypto API helpers
    jwt.ts              # signJWT, verifyJWT, decodeJWT
    schema.ts           # Auth SQL schema (CREATE IF NOT EXISTS)
    types.ts            # User, Session, AuthResponse
  storage/              # Storage API emulation
    handler.ts          # Bucket/object CRUD
    backend.ts          # StorageBackend interface + MemoryStorageBackend
    schema.ts           # Storage SQL schema
  fetch-adapter/        # Intercepts supabase-js fetch calls
    index.ts            # createLocalFetch: URL routing
    auth-routes.ts      # /auth/v1/* dispatch
    data-routes.ts      # /rest/v1/* dispatch
    storage-routes.ts   # /storage/v1/* dispatch
    auth-context.ts     # JWT claims extraction for RLS
    error-handler.ts    # Error -> Response conversion
tests/
  compat.ts             # Deno/Bun test shim (runtime-agnostic)
  *.test.ts             # Split by concern: auth, data, storage, fetch-adapter, applications, full-user-flow

Key Technical Details

  • TypeScript strict mode, ESNext modules, ES2022 target
  • Cross-runtime: Node.js, Deno, Bun, Browser, Cloudflare Workers. Use Web Crypto API only (no Node crypto)
  • PGlite is a peer dependency - users provide their own instance
  • postgrest-parser bundled as WASM in dist/
  • esbuild bundles to dist/index.js (~21KB) + WASM (~377KB)
  • SQL schemas use CREATE IF NOT EXISTS for idempotent initialization
  • RLS functions: auth.uid(), auth.role(), auth.email() backed by PostgreSQL roles (anon, authenticated, service_role)

Common Patterns

Create a client:

const client = createLocalSupabaseClient(db);
// or manually:
const client = createClient(url, key, { global: { fetch: createLocalFetch(db, key) } });

Auth flow:

await supabase.auth.signUp({ email, password });
const { data: { session } } = await supabase.auth.signInWithPassword({ email, password });
// JWT now in session, RLS active

Data with RLS:

// Create table + RLS policy, then query as authenticated user
const { data } = await supabase.from('todos').select('*');
// Only returns rows matching RLS policy

Storage:

await supabase.storage.from('avatars').upload('path/file.png', blob);
const { data } = await supabase.storage.from('avatars').download('path/file.png');
const { data: { signedUrl } } = await supabase.storage.from('avatars').createSignedUrl('path/file.png', 3600);

Testing

  • Behavior over implementation: Test real user workflows, not internals
  • No helper files: Inline all setup for full context at each test
  • No excessive mocking: Use real PGlite instances
  • Each test creates its own PGlite instance + client (isolated)
  • Full workflows: signup -> insert -> query -> verify RLS -> cleanup
  • Runtime-agnostic via compat.ts shim

Run tests:

deno test --allow-read --allow-env tests/
bun test tests/

Build

npm run build          # esbuild -> dist/
npm run build:dev      # tsc only
npm run build:types    # declarations

CI: GitHub Actions matrix (Bun + Deno), tests split by concern.

Rules

  • No comments in code
  • No helper/utility files
  • No Node.js-specific APIs
  • Test behavior, not implementation
  • Do not test what the compiler verifies

Categories