
studio-queries
✓ Official★ 105,700by supabase · part of supabase/supabase
React Query conventions for data fetching in Supabase Studio. Use when
React Query conventions for data fetching in Supabase Studio. Use when
Inspect the full instructions your agent will receiveExpandCollapse
This is the exact playbook injected into your agent when the skill activates — shown here so you can audit it before installing. You don't need to read it to use the skill.
name: studio-queries description: React Query conventions for data fetching in Supabase Studio. Use when writing or reviewing query hooks, mutation hooks, or query keys in apps/studio/data/. Covers queryOptions pattern, keys.ts structure, mutation hook template, and imperative fetching.
Studio Queries & Mutations (React Query)
Follow the patterns in apps/studio/data/. Reference examples:
- Query options:
apps/studio/data/table-editor/table-editor-query.ts - Mutation hook:
apps/studio/data/edge-functions/edge-functions-update-mutation.ts - Keys:
apps/studio/data/edge-functions/keys.ts
Query Keys
Define a keys.ts per domain. Export *Keys helpers using array keys with as const. Never inline query keys in components.
export const edgeFunctionsKeys = {
list: (projectRef: string | undefined) => ['projects', projectRef, 'edge-functions'] as const,
detail: (projectRef: string | undefined, slug: string | undefined) =>
['projects', projectRef, 'edge-function', slug, 'detail'] as const,
}Query Options (preferred pattern)
Use queryOptions from @tanstack/react-query. This gives type safety and works with both useQuery() and queryClient.fetchQuery().
Rules:
- Export
XVariables,XData, andXErrortypes (prefixed with the domain name) - Implement a private
getX(variables, signal?)function:- Throws if required variables are missing
- Passes
signalfor cancellation - Calls
handleError(error)on failure (which throws); returnsdataon success - Not exported — use
queryClient.fetchQuery(xQueryOptions(...))for imperative fetching
- Export
xQueryOptions()usingqueryOptions - Gate with
enabledso the query doesn't run until required variables exist - Platform-only queries: include
IS_PLATFORMfromlib/constantsinenabled - Don't add extra params to
xQueryOptions— callers override by destructuring:{ ...xQueryOptions(vars), enabled: true }
import { queryOptions } from '@tanstack/react-query'
import { xKeys } from './keys'
import { get, handleError } from '@/data/fetchers'
import { IS_PLATFORM } from '@/lib/constants'
import { ResponseError } from '@/types'
export type XVariables = { projectRef?: string }
export type XError = ResponseError
async function getX({ projectRef }: XVariables, signal?: AbortSignal) {
if (!projectRef) throw new Error('projectRef is required')
const { data, error } = await get('/v1/projects/{ref}/x', {
params: { path: { ref: projectRef } },
signal,
})
if (error) handleError(error)
return data
}
export type XData = Awaited<ReturnType<typeof getX>>
export const xQueryOptions = ({ projectRef }: XVariables) =>
queryOptions({
queryKey: xKeys.list(projectRef),
queryFn: ({ signal }) => getX({ projectRef }, signal),
enabled: IS_PLATFORM && typeof projectRef !== 'undefined',
})Using Query Options in Components
import { useQuery } from '@tanstack/react-query'
import { xQueryOptions } from '@/data/x/x-query'
const { data, isPending, isError } = useQuery(xQueryOptions({ projectRef: project?.ref }))Imperative Fetching (outside React or in callbacks)
const queryClient = useQueryClient()
const { data: project } = useSelectedProjectQuery()
const handleClick = useCallback(
async (id: number) => {
const data = await queryClient.fetchQuery(xQueryOptions({ id, projectRef: project?.ref }))
// use data...
},
[project?.ref, queryClient]
)Mutation Hook
- Export a
Variablestype withprojectRef, identifiers, andpayload - Implement a private
updateX(vars)function with required variable validation andhandleError - Wrap in
useXMutation():- Accepts
UseMutationOptions(omitmutationFn) - Invalidates
list()+detail()keys inonSuccesswithawait Promise.all([...]) - Defaults to
toast.error(...)whenonErrorisn't provided
- Accepts
import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query'
import toast from 'react-hot-toast'
import { xKeys } from './keys'
type XUpdateVariables = { projectRef: string; slug: string; payload: XPayload }
export const useXUpdateMutation = ({
onSuccess,
onError,
...options
}: UseMutationOptions<XData, XError, XUpdateVariables> = {}) => {
const queryClient = useQueryClient()
return useMutation({
mutationFn: updateX,
async onSuccess(data, variables, context) {
await Promise.all([
queryClient.invalidateQueries({
queryKey: xKeys.detail(variables.projectRef, variables.slug),
}),
queryClient.invalidateQueries({ queryKey: xKeys.list(variables.projectRef) }),
])
await onSuccess?.(data, variables, context)
},
async onError(error, variables, context) {
if (onError === undefined) toast.error(`Failed to update: ${error.message}`)
else onError(error, variables, context)
},
...options,
})
}npx skills add https://github.com/supabase/supabase --skill studio-queriesRun this in your project — your agent picks the skill up automatically.
Component Usage
- Use React Query v5 flags:
isPendingfor initial load,isFetchingfor background refetches - Render states explicitly in order: pending → error → success
No common issues documented yet. If you hit a problem, the repository's GitHub Issues page is the best place to look.