React hooks
Use TanStack Query hooks for Database, Functions, and Storage work.
The React entry point exposes hooks that use the CarpoProvider client and TanStack Query cache. Place QueryClientProvider above CarpoProvider and call Carpo hooks below them.
Authentication and context
useCarpoClient()returns the configured client.useCarpoAuth()returns its native Better Auth React client.
Use Better Auth's useSession() hook for reactive session state. The Carpo provider listens to that session and scopes service query keys by the current identity.
Database reads
| Hook | Purpose |
|---|---|
useDatabaseTables(options?) | Read visible tables and permissions |
useDatabaseRows(table, selectOptions?, queryOptions?) | Fetch one bounded result page |
useLiveDatabaseRows(table, selectOptions?, queryOptions?) | Fetch rows and invalidate them after realtime changes |
useInfiniteDatabaseRows(table, selectOptions?, queryOptions?) | Fetch page number results with fetchNextPage() |
useCursorDatabaseRows(table, selectOptions?, queryOptions?) | Fetch successive pages with keyset cursors |
Reads accept TanStack Query options such as enabled, select, staleTime, and placeholderData. Carpo provides the query key and query function, and passes TanStack Query's abort signal through to the API request.
Database writes
| Hook | Mutation input |
|---|---|
useInsertDatabaseRow(table, options?) | Row values |
useInsertManyDatabaseRows(table, options?) | { rows, returning? } |
useUpsertDatabaseRows(table, options?) | { rows, options } where options includes conflict |
useUpdateDatabaseRows(table, options?) | { where, values } |
useDeleteDatabaseRows(table, options?) | where conditions |
useInvokeFunction(options?) | { functionId, input? }, parsed as JSON |
Write hooks accept TanStack mutation options and invalidate the affected table's query keys after a successful write. useInvokeFunction() uses invokeJson(), so it expects a JSON response and reports non successful responses as errors.
Bind hooks to a schema
createCarpoDatabaseHooks<AppDatabase>() returns schema aware versions of useTables, useRows, useLiveRows, useInfiniteRows, useCursorRows, useInsert, useInsertMany, useUpsert, useUpdate, and useDelete.
const db = createCarpoDatabaseHooks<AppDatabase>()
function Todos() {
const todos = db.useRows('todos', {
where: { completed: false },
limit: 25,
})
const addTodo = db.useInsert('todos')
return (
<>
<button onClick={() => addTodo.mutate({ title: 'Ship the change' })}>
Add todo
</button>
<pre>{JSON.stringify(todos.data?.rows ?? [], null, 2)}</pre>
</>
)
}Schema types are compile time only. Server table permissions and schema validation remain authoritative.
Storage hooks
Storage hooks are bucket and user scoped. Infinite variants use cursor pagination. Mutations invalidate the affected object, listing, version, or trash keys.
| Area | Hooks |
|---|---|
| Client | useCarpoStorage() |
| Listings | useStorageObjects(), useInfiniteStorageObjects() |
| Object detail | useStorageObjectMetadata(), useStorageObjectExists() |
| Recovery reads | useStorageObjectVersions(), useInfiniteStorageObjectVersions(), useStorageTrash(), useInfiniteStorageTrash() |
| Mutations | useUploadStorageObject(), useUpdateStorageObjectMetadata(), useDeleteStorageObjects(), useCopyStorageObject(), useMoveStorageObject(), useRestoreStorageVersion(), useDeleteStorageVersion() |
| Downloads | useCreateStorageDownloadUrl(), useDownloadStorageObject() |
useUploadStorageObject() takes { file, key, ...options }. Its options match the direct storage.upload(file, options) method. Every Storage hook requires storageBucketId on the combined client.