Skip to main content

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 Refine list, detail, and form views:

  • list, read-one, and read-many hooks;
  • create, update, and delete hooks, including upserts and filtered deletes through meta;
  • aggregates through meta.aggregate, meta.groupBy, and meta.having;
  • vector and hybrid queries through explicit flat query parameters;
  • flat and nested AND/OR filters;
  • 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,
});

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. Never put an 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 to compose nested and and or groups. For negation, use inverse operators such as ne, nin, ncontains, or nnull, or send the serialized tree shown in Negate a condition.

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 inputTaruviBase request
resourceTable name
useMany.idsin filter on id, or meta.idColumnName
pagination.currentPagepage
pagination.pageSizepage_size
sortersordering
Field filtersfield or field__operator
meta.selectfields
meta.tableNameTable-name override
meta.searchsearch
meta.allowedActionsallowed_actions

Keep pagination in server mode for paged TaruviBase collections. Use pagination: {mode: 'off'} only when the request should omit pagination parameters.

Search and aggregate#

Use full-text, vector, and hybrid queries for ranked or keyword retrieval. Full-text search uses meta.search; vector and hybrid controls use flat eq filters with the API's exact query parameter names. Keep those controls outside nested logical groups.

Use aggregation metadata to count, group, calculate summaries, and filter groups. Summary rows are in result.data; for an ungrouped count, read result.data[0].count after success.

Write records#

useCreate, useUpdate, and useDelete create, update, and delete records; useCreateMany, useUpdateMany, and useDeleteMany work on several at once. meta.upsert inserts or updates on the primary key, and meta.deleteByFilter deletes every record that matches meta.filters. See the Refine data provider reference. Use TaruviBase Console for one-off changes.

Follow the record examples for hook bindings and event handlers. For batches, see the bulk-operation examples: useCreateMany sends separate requests, useUpdateMany applies one shared values object, and useDeleteMany returns the submitted IDs. Read those IDs again to confirm deletion.

Continue#