Database Data Provider
The primary provider for CRUD operations on database tables.
Setup
<Refine dataProvider={dataProvider(client)} resources={[{ name: "posts" }]} />
Return Values
All hooks return { result, query } (for queries) or { mutate, mutation } (for mutations), following Refine v5 conventions.
Query Hooks (useList, useOne, useMany)
const { result, query } = useList({ resource: "posts" });
// result — the resolved data
result.data; // TData[] — array of records
result.total; // number — total record count (for pagination)
// query — TanStack Query state
query.isLoading; // boolean — true during initial fetch
query.isError; // boolean — true if the query failed
query.error; // HttpError | null — error details
query.isRefetching;// boolean — true during background refetch
query.refetch; // () => void — manually re-run the query
const { result, query } = useOne({ resource: "posts", id: 1 });
result.data; // TData — single record object
const { result, query } = useMany({ resource: "posts", ids: [1, 2, 3] });
result.data; // TData[] — array of records
Mutation Hooks (useCreate, useUpdate, useDelete)
const { mutate, mutation } = useCreate();
mutate(
{ resource: "posts", values: { title: "Hello" } },
{
onSuccess: (result) => {
result.data; // TData — the created record
},
onError: (error) => {
error.message; // string
error.statusCode; // number
},
}
);
// mutation — TanStack Query mutation state
mutation.isLoading; // boolean — true while mutating
mutation.isError; // boolean
mutation.isSuccess; // boolean
const { mutate, mutation } = useUpdate();
mutate({
resource: "posts",
id: 1,
values: { title: "Updated" },
});
// onSuccess result.data → the updated record
const { mutate, mutation } = useDelete();
mutate({ resource: "posts", id: 1 });
// onSuccess result.data → the deleted record (if returned by backend)
CRUD Operations
// List records
const { result, query } = useList({ resource: "posts" });
// result.data → IPost[], result.total → number
// query.isLoading, query.isError, query.error
// Get single record
const { result, query } = useOne({ resource: "posts", id: 1 });
// result.data → IPost
// Get multiple records
const { result, query } = useMany({ resource: "posts", ids: [1, 2, 3] });
// result.data → IPost[]
// Create
const { mutate, mutation } = useCreate();
mutate({ resource: "posts", values: { title: "Hello" } });
// mutation.isLoading, mutation.isSuccess, mutation.isError
// Update
const { mutate, mutation } = useUpdate();
mutate({ resource: "posts", id: 1, values: { title: "Updated" } });
// Delete
const { mutate, mutation } = useDelete();
mutate({ resource: "posts", id: 1 });
Filtering
const { result, query } = useList({
resource: "posts",
filters: [
{ field: "status", operator: "eq", value: "published" },
{ field: "views", operator: "gte", value: 100 },
{ field: "title", operator: "contains", value: "refine" },
{ field: "category", operator: "in", value: ["tech", "news"] },
],
});
// result.data → filtered IPost[], result.total → filtered count
Supported filter operators:
| Operator | SDK Mapping | Description |
|---|---|---|
eq | eq | Equal |
ne | ne | Not equal |
lt, gt, lte, gte | lt, gt, lte, gte | Comparison |
contains | contains | Contains (case-sensitive) |
containss | icontains | Contains (case-insensitive) |
startswith | startswith | Starts with (case-sensitive) |
startswiths | istartswith | Starts with (case-insensitive) |
endswith | endswith | Ends with (case-sensitive) |
endswiths | iendswith | Ends with (case-insensitive) |
in | in | In array |
nin | nin | Not in array |
null | isnull | Is null |
Filtering on Related Records
Use dot notation in the field to filter by a property of a foreign-keyed (related) record:
const { result } = useList({
resource: "activities",
filters: [
// Forward FK: activities whose related deal's name contains "Acme"
{ field: "deal_id.name", operator: "contains", value: "Acme" },
// Multi-hop: activities whose deal's company name starts with "Acme"
{ field: "deal_id.company_id.name", operator: "startswith", value: "Acme" },
],
});
// Reverse FK: deals that have at least one matching activity
const { result: dealsWithFollowups } = useList({
resource: "deals",
filters: [
{ field: "activities.subject", operator: "contains", value: "follow up" },
],
});
// Many-to-many via junction table (transparent to the caller)
const { result: admins } = useList({
resource: "users",
filters: [
{ field: "roles.name", operator: "contains", value: "admin" },
],
});
The related row is not added to the response payload unless you also pass populate for the same relation. See Filtering on Related Records in the API guide for SQL details, limits, and edge cases.
Sorting & Pagination
const { result, query } = useList({
resource: "posts",
sorters: [
{ field: "created_at", order: "desc" },
{ field: "title", order: "asc" },
],
pagination: { currentPage: 1, pageSize: 20 },
});
// result.data → sorted/paginated IPost[], result.total → total count
Meta Options
The meta parameter accepts TaruviMeta for Taruvi-specific features:
const { result, query } = useList({
resource: "posts",
meta: {
tableName: "blog_posts", // override table name
populate: ["author", "category"], // populate foreign keys (or "*" for all)
select: ["id", "title", "status"], // select specific fields
idColumnName: "post_id", // custom primary key column
},
});
// result.data → IPost[] with populated author & category objects
| Meta Option | Type | Default | Description |
|---|---|---|---|
tableName | string | resource name | Override the database table name |
populate | string | string[] | — | Foreign key fields to populate. "*" for all |
select | string | string[] | — | Fields to return |
idColumnName | string | "id" | Custom primary key column name |
headers | Record<string, string> | — | Custom request headers |
Aggregations
const { result, query } = useList({
resource: "orders",
meta: {
aggregate: ["sum(total)", "count(*)", "avg(quantity)"],
groupBy: ["status", "category"],
having: [{ field: "sum(total)", operator: "gte", value: 1000 }],
},
});
// result.data → aggregated rows with sum_total, count, avg_quantity fields
Supported aggregate functions: sum, avg, count, min, max, array_agg, string_agg, json_agg, stddev, variance.
Graph Operations
When any graph meta option is present (format, include, depth, graph_types), the provider switches from Database to Graph SDK class.
Reading Graph Data
// Get graph structure for a record
const { result, query } = useOne({
resource: "employees",
id: "1",
meta: {
format: "graph", // "tree" or "graph"
include: "descendants", // "descendants", "ancestors", or "both"
depth: 2,
graph_types: ["manager", "mentor"],
},
});
// result.data → graph/tree structure with nested nodes
// List with graph format
const { result, query } = useList({
resource: "employees",
meta: { format: "tree", include: "both", depth: 3 },
});
// result.data → array of tree-structured records
| Meta Option | Type | Description |
|---|---|---|
format | "tree" | "graph" | Output format |
include | "descendants" | "ancestors" | "both" | Traversal direction |
depth | number | Traversal depth |
graph_types | string[] | Filter edges by type |
Managing Graph Edges
When graph meta is present, mutations operate on edges instead of records:
// Create edge
const { mutate } = useCreate();
mutate({
resource: "employees",
values: { from_id: 1, to_id: 2, type: "manager", metadata: { since: "2024-01-01" } },
meta: { format: "graph" },
});
// Update edge
const { mutate } = useUpdate();
mutate({
resource: "employees",
id: "edge-123",
values: { from_id: 1, to_id: 3, type: "manager" },
meta: { format: "graph" },
});
// Delete edge
const { mutate } = useDelete();
mutate({ resource: "employees", id: "edge-123", meta: { format: "graph" } });
// Delete multiple edges
const { mutate } = useDeleteMany();
mutate({ resource: "employees", ids: ["edge-123", "edge-456"], meta: { format: "graph" } });