Skip to content
Back to skills

Supabase Query System

ASecurity

Convention for reading data from Supabase/PostgreSQL using a query registry with centralized key factories, the Supabase browser client, and TanStack React Query caching. Read this when working on data fetching, queries, dashboards, or data display.

  • 2 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 17, 2026
developmentgosqlreactnextjsapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned October 17, 2026

npx -y skills add Bobi-Labs/bobi-stack-template --skill supabase-query-system --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Supabase Query System?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Supabase Query System
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/bobi-labs-supabase-query-system/badge)](https://www.skillsdirectory.com/skills/bobi-labs-supabase-query-system)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: supabase-query-system
description: Convention for reading data from Supabase/PostgreSQL using a query registry with centralized key factories, the Supabase browser client, and TanStack React Query caching. Read this when working on data fetching, queries, dashboards, or data display.
user-invocable: true
---

# Supabase Query System: Read Path

A type-safe, cached data fetching pattern for Next.js + Supabase. Query keys live in a centralized factory (`lib/query-keys.ts`) shared by both reads and mutations. A registry pairs each key with its query function. The hook enforces this pairing so you can't mismatch keys and functions or typo a key. RLS auto-filters results.

## Architecture

```
React Component
    ↓ useClientQuery("items")              ← hooks/use-client-query.ts
    ↓ looks up registry → key + fn         ← lib/queries/index.ts
    ↓ key factory builds query key         ← lib/query-keys.ts
    ↓ fn creates browser Supabase          ← lib/supabase/client.ts
    ↓ Supabase SDK .from().select()        ← RLS auto-applies
    ↓ TanStack Query client cache          ← staleTime + gcTime
    ↓ component re-renders with data
```

## Core files

### Query key factories: `lib/query-keys.ts`

Single source of truth for all cache keys. Shared by queries (pairing) and mutations (invalidation). Keys are functions so they can accept params.

```ts
export const queryKeys = {
  items: () => ["items"] as const,
  item: (id: string) => ["item", id] as const,
} as const;
```

### Query registry: `lib/queries/index.ts`

Pairs each key factory with its query function. The hook enforces this: you can only call `useClientQuery` with a registered name.

```ts
import { queryKeys } from "@/lib/query-keys";
import { getItems } from "./get-items";

export const queries = {
  items: {
    key: queryKeys.items,
    fn: getItems,
  },
} as const;

export type QueryName = keyof typeof queries;
```

### Client hook: `hooks/use-client-query.ts`

Type-safe hook that enforces registry pairing. Params are type-checked against the key factory.

```ts
export default function useClientQuery<K extends QueryName>(
  name: K,
  ...args: Parameters<(typeof queries)[K]["key"]>
) {
  const { key, fn } = queries[name];
  return useQuery({
    queryKey: [...(key as (...a: any[]) => readonly unknown[])(...args)],
    queryFn: () => (fn as (...a: any[]) => Promise<any>)(...args),
  });
}
```

## Adding a new query

Use `/new-query` to scaffold all files automatically:

```
/new-query items                                      # all columns, no params
/new-query items id,name,status                       # specific columns
/new-query items id,name --param categoryId:string    # with filter param
```

### Manual steps (3 steps)

1. Add key factory in `lib/query-keys.ts`
2. Create query function in `lib/queries/get-{name}.ts` + register in `lib/queries/index.ts`
3. Use in component: `useClientQuery("items")`

## Key rules

- **All query keys live in `lib/query-keys.ts`**, shared by queries and mutations
- **Registry pairs keys with functions**: `useClientQuery` only accepts registered names
- **Params must match** between the key factory and query function
- **Queries run client-side**, so RLS auto-filters results
- **No raw SQL**: use the Supabase SDK fluent API
- Throw on Supabase errors and let TanStack Query handle error state
- Run `./ops.sh gen-types` after schema changes
- Run `./ops.sh test` after adding/changing queries

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.