Connect a Refine application
@taruvi/refine-providers connects Refine data hooks to app-scoped TaruviBase
tables. The provider maps Refine resources, filters, sorters, pagination, and
query metadata to the same Database API the JavaScript SDK uses.
Use the provider for read-focused Refine views:
- list, read-one, and read-many hooks;
- flat and nested
AND/ORfilters; - sorting and server pagination;
- field projection; and
- per-record action hints.
Install the packages
npm install \
@refinedev/core \
@taruvi/refine-providers \
@taruvi/sdk
Register the provider
Create one browser client and pass it to dataProvider. The resource name
maps to the table name unless a hook supplies meta.tableName.
import {Refine} from '@refinedev/core';
import {dataProvider} from '@taruvi/refine-providers';
import {Auth, Client} from '@taruvi/sdk';
const client = new Client({
apiUrl: import.meta.env.VITE_TARUVI_SITE_URL,
appSlug: import.meta.env.VITE_TARUVI_APP_SLUG,
apiKey: 'session-authenticated-client',
});
const auth = new Auth(client);
export function App() {
if (!auth.hasToken()) {
return <button onClick={() => auth.login()}>Sign in to TaruviBase</button>;
}
return (
<Refine
dataProvider={dataProvider(client)}
resources={[{name: 'tasks'}]}
/>
);
}
The JavaScript SDK captures the hosted-login callback's session_token in
the browser, stores it locally, and sends it through X-Session-Token. Do not
pass the token constructor option in browser code; that option is read only
by non-browser runtimes. The session-authenticated-client value is a
non-secret constructor placeholder and is not transmitted; never put a real
API key in browser code.
Define the record type
Refine records need an id. Match the remaining fields to the TaruviBase table
schema:
type Task = {
id: string;
title: string;
done: boolean;
};
Import the hooks and filter helper once, then keep each task example focused on the operation:
import {useList, useMany, useOne} from '@refinedev/core';
import {toRefineFilters} from '@taruvi/refine-providers';
List records
Place data hooks inside a component rendered beneath <Refine>.
useList<Task>({
resource: 'tasks',
filters: [{field: 'done', operator: 'eq', value: false}],
sorters: [
{field: 'title', order: 'asc'},
{field: 'id', order: 'asc'},
],
pagination: {
currentPage: 1,
pageSize: 20,
mode: 'server',
},
});
Refine 5 exposes the records and matching count through result.data and
result.total. Request state is available through query.
Read one record
useOne<Task>({
resource: 'tasks',
id: 'TASK_UUID',
});
The provider calls the single-record Database route and maps its data object
to the hook result.
Read several records by ID
Use useMany when a view already has a bounded set of primary keys. In this
example, taskIds is an array of task UUIDs.
useMany<Task>({
resource: 'tasks',
ids: taskIds,
});
Refine 5 exposes the returned records through result.data. TaruviBase's provider
maps useMany to one Database list query with an in filter on id; set
meta.idColumnName only when the table's primary key isn't id.
Select fields
Use TaruviBase metadata when a query needs only part of each record:
useList<Pick<Task, 'id' | 'title' | 'done'>>({
resource: 'tasks',
pagination: {currentPage: 1, pageSize: 20, mode: 'server'},
meta: {
select: ['id', 'title', 'done'],
},
});
Compose nested filters
Use toRefineFilters when a query includes TaruviBase-specific operators or a
nested logical group. The provider converts the logical object to the
Database JSON filter tree.
useList<Task>({
resource: 'tasks',
filters: toRefineFilters({
operator: 'and',
value: [
{field: 'done', operator: 'eq', value: false},
{
operator: 'or',
value: [
{field: 'title', operator: 'contains', value: 'release'},
{field: 'title', operator: 'contains', value: 'docs'},
],
},
],
}),
pagination: {currentPage: 1, pageSize: 20, mode: 'server'},
});
Import toRefineFilters from @taruvi/refine-providers. The filter
model supports nested and and or groups. It does not define a logical
not node; for negation, use operators such as ne,
nin, ncontains, or nnull.
Read related records
Read the root rows first, collect their foreign-key values, and issue a
second bounded useList query for the related table, so each table gets its
own access check. Join the results by their foreign-key values in the component
or data layer. See the complete
related-record workflow.
Request allowed actions
The provider maps meta.allowedActions to the Database
allowed_actions query parameter:
useList<Task>({
resource: 'tasks',
meta: {allowedActions: ['update', 'delete']},
pagination: {currentPage: 1, pageSize: 20, mode: 'server'},
});
When enabled by the service, each row can include an _allowed_actions array
for UI affordances. Treat it as a hint for the current response; every
mutation is still authorized by the server when it is submitted.
Provider mapping
| Refine input | TaruviBase request |
|---|---|
resource | Table name |
useMany.ids | in filter on id, or meta.idColumnName |
pagination.currentPage | page |
pagination.pageSize | page_size |
sorters | ordering |
| Field filters | field or field__operator |
meta.select | fields |
meta.tableName | Table-name override |
meta.search | search |
meta.allowedActions | allowed_actions |
Keep pagination in server mode for paged TaruviBase collections. Use
pagination: {mode: 'off'} only when the request should omit pagination
parameters.
Choose an interface for writes
Use Refine for list, detail, filter, and selection views. Submit application writes through the JavaScript SDK, Python SDK, or REST API, and use TaruviBase Console for one-off changes.