absent-sapphire
absent-sapphire14mo ago

how to pass additional "meta" for resources to dataProvider?

I'm writing a new dataProfider for the WordPress REST API. I need to pass additional parameters for the resource to the dataProvider. Thans to the help of kapa.ai in this thread: https://discord.com/channels/837692625737613362/1093124784801468487 I tried to add them as meta in <Refine resources{}> but I can't get it in the dataProvider. What I'm doing wrong? here the relevant code, the parameter I need to get in dataProvider is :namespace

function App() {
return (
<BrowserRouter>
<GitHubBanner />
<RefineKbarProvider>
<Refine
dataProvider={dataProvider("{mysite}/wp-json")}
routerProvider={routerBindings}
authProvider={authProvider}
resources={[
{
name: "posts",
list: "/blog-posts",
create: "/blog-posts/create",
edit: "/blog-posts/edit/:id",
show: "/blog-posts/show/:id",
meta: {
canDelete: true,
namespace: 'wp/v2/'
},
},
]}
options={{
syncWithLocation: true,
warnWhenUnsavedChanges: true,
}}
>
...
...

function App() {
return (
<BrowserRouter>
<GitHubBanner />
<RefineKbarProvider>
<Refine
dataProvider={dataProvider("{mysite}/wp-json")}
routerProvider={routerBindings}
authProvider={authProvider}
resources={[
{
name: "posts",
list: "/blog-posts",
create: "/blog-posts/create",
edit: "/blog-posts/edit/:id",
show: "/blog-posts/show/:id",
meta: {
canDelete: true,
namespace: 'wp/v2/'
},
},
]}
options={{
syncWithLocation: true,
warnWhenUnsavedChanges: true,
}}
>
...
...
and in dataProvider:
export const dataProvider = (
apiUrl: string,
httpClient: AxiosInstance = axiosInstance
): Omit<
Required<DataProvider>,
"createMany" | "updateMany" | "deleteMany"
> => ({

getList: async ({ resource, pagination, filters, sorters, meta }) => {
const { namespace } = meta;
console.log('namespace', namespace);

const url = `${apiUrl}/${namespace}/${resource}`;

const { current = 1, pageSize = 10, mode = "server" } = pagination ?? {};
...
...
export const dataProvider = (
apiUrl: string,
httpClient: AxiosInstance = axiosInstance
): Omit<
Required<DataProvider>,
"createMany" | "updateMany" | "deleteMany"
> => ({

getList: async ({ resource, pagination, filters, sorters, meta }) => {
const { namespace } = meta;
console.log('namespace', namespace);

const url = `${apiUrl}/${namespace}/${resource}`;

const { current = 1, pageSize = 10, mode = "server" } = pagination ?? {};
...
...
14 Replies
kapa.ai
kapa.ai14mo ago
Hi @_garubi. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
kapa.ai
kapa.ai14mo ago
---------------------- It seems you are trying to pass the namespace parameter through the meta property in the <Refine> component's resources. However, the meta property in the <Refine> component's resources is not directly passed to the data provider methods. Instead, you should pass the namespace parameter using the meta property in the hooks that call the data provider methods. For example, if you are using the useList hook, you can pass the namespace like this:
const { data } = useList({
resource: "posts",
meta: {
namespace: "wp/v2/",
},
});
const { data } = useList({
resource: "posts",
meta: {
namespace: "wp/v2/",
},
});
Then, in your data provider, you can access the namespace from the meta object:
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const { namespace } = meta;
console.log('namespace', namespace);

const url = `${apiUrl}/${namespace}/${resource}`;

// ...
},
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const { namespace } = meta;
console.log('namespace', namespace);

const url = `${apiUrl}/${namespace}/${resource}`;

// ...
},
Remember to pass the meta with the namespace in all the hooks that call the data provider methods, such as useOne, useCreate, useUpdate, etc. For more information on how to use meta to pass additional information to data provider methods, you can refer to the general concepts documentation (https://refine.dev/docs/api-reference/general-concepts/#meta). ---------------------- Note: you can ask me a follow up question by @ mentioning me again :speech_balloon: ----------------------
General Concepts | refine
- refine core is fully independent of UI. So you can use core components and hooks without any UI dependency.
kapa.ai
kapa.ai14mo ago
<@976939425848250478> kapa.ai is still learning and improving, please let us how it did by reacting below
foreign-sapphire
foreign-sapphire14mo ago
Hey @_garubi Parameters sent from data hooks such as useList are passed for the meta parameter in the data provider. https://refine.dev/docs/api-reference/core/hooks/data/useList/#meta
useList | refine
useList is an extended version of TanStack Query's useQuery. It supports all the features of useQuery and adds some extra features.
absent-sapphire
absent-sapphire14mo ago
Thank you @yildirayunlu ! I'll give it a try in a few minutes.. @yildirayunlu tryed...but still no luck 😕 I'm sending the meta in useTable this way:
const {
getHeaderGroups,
getRowModel,
setOptions,
refineCore: {
tableQueryResult: { data: tableData },
},
getState,
setPageIndex,
getCanPreviousPage,
getPageCount,
getCanNextPage,
nextPage,
previousPage,
setPageSize,
getColumn,
} = useTable({
columns,
meta: {
namespace: "wp/v2/",
},
});
const {
getHeaderGroups,
getRowModel,
setOptions,
refineCore: {
tableQueryResult: { data: tableData },
},
getState,
setPageIndex,
getCanPreviousPage,
getPageCount,
getCanNextPage,
nextPage,
previousPage,
setPageSize,
getColumn,
} = useTable({
columns,
meta: {
namespace: "wp/v2/",
},
});
and try to retrieve it in dataProvider, but I get nothing:
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const namespace = meta?.namespace ?? {};
console.log('namespace', namespace);
...
...
getList: async ({ resource, pagination, filters, sorters, meta }) => {
const namespace = meta?.namespace ?? {};
console.log('namespace', namespace);
...
...
foreign-sapphire
foreign-sapphire14mo ago
https://refine.dev/docs/api-reference/core/hooks/useTable/#meta Here is a more detailed example. Also, do other parameters (resource, pagination) come here?
rival-black
rival-black14mo ago
Hey @_garubi, You need to send meta in refineCoreProps prop. 🤞🏻
absent-sapphire
absent-sapphire14mo ago
Thank you, yes I started yesterday with it 😆 ... I re-done it now, without any luck... I can confirm that pagination comes in and I got it working. Here the code as it is now:
import React from "react";
import {
IResourceComponentsProps,
useNavigation,
GetManyResponse,
useMany,
} from "@refinedev/core";
import { useTable } from "@refinedev/react-table";
import { ColumnDef, flexRender } from "@tanstack/react-table";

export const BlogPostList: React.FC<IResourceComponentsProps> = () => {
const columns = React.useMemo<ColumnDef<any>[]>(
() => [
{
id: "id",
accessorKey: "id",
header: "Id",
},
{
id: "title",
accessorKey: "title.rendered",
header: "Title",
},],
[],
);

const {
getHeaderGroups,
getRowModel,
setOptions,
refineCore: {
tableQueryResult: { data: tableData },
},
getState,
setPageIndex,
getCanPreviousPage,
getPageCount,
getCanNextPage,
nextPage,
previousPage,
setPageSize,
getColumn,
} = useTable({
columns,
meta: {
headers: { "x-meta-data": "true" },
},
});

...
import React from "react";
import {
IResourceComponentsProps,
useNavigation,
GetManyResponse,
useMany,
} from "@refinedev/core";
import { useTable } from "@refinedev/react-table";
import { ColumnDef, flexRender } from "@tanstack/react-table";

export const BlogPostList: React.FC<IResourceComponentsProps> = () => {
const columns = React.useMemo<ColumnDef<any>[]>(
() => [
{
id: "id",
accessorKey: "id",
header: "Id",
},
{
id: "title",
accessorKey: "title.rendered",
header: "Title",
},],
[],
);

const {
getHeaderGroups,
getRowModel,
setOptions,
refineCore: {
tableQueryResult: { data: tableData },
},
getState,
setPageIndex,
getCanPreviousPage,
getPageCount,
getCanNextPage,
nextPage,
previousPage,
setPageSize,
getColumn,
} = useTable({
columns,
meta: {
headers: { "x-meta-data": "true" },
},
});

...
dataProvider:
export const dataProvider = (
apiUrl: string,
httpClient: AxiosInstance = axiosInstance
): Omit<
Required<DataProvider>,
"createMany" | "updateMany" | "deleteMany"
> => ({

getList: async ({ resource, pagination, filters, sorters, meta }) => {
const header = meta?.headers ?? {};
console.log('header', header);
export const dataProvider = (
apiUrl: string,
httpClient: AxiosInstance = axiosInstance
): Omit<
Required<DataProvider>,
"createMany" | "updateMany" | "deleteMany"
> => ({

getList: async ({ resource, pagination, filters, sorters, meta }) => {
const header = meta?.headers ?? {};
console.log('header', header);
btw How can I enable syntax highlighting in code bloks? (I'm pretty new to Discord)
rival-black
rival-black14mo ago
If you use useTable from @refinedev/react-table you need pass props like below:
import { useTable } from "@refinedev/react-table";

useTable({
refineCoreProps: {
metaData: {
headers: { "x-meta-data": "true" },
},
},
});
import { useTable } from "@refinedev/react-table";

useTable({
refineCoreProps: {
metaData: {
headers: { "x-meta-data": "true" },
},
},
});
Here is useTable doc imported from @refinedev/react-table: https://refine.dev/docs/packages/documentation/react-table/#meta
absent-sapphire
absent-sapphire14mo ago
Oh... now I understand!! 🤦‍♂️ It's working now!! thank you 🙂 Isn't there a way to send this "meda" from the rsource object in the app's <Refine> component? I ask because, while sending parameter to dataProvider inside ute "useXX>" hook is ok for query related parameter, I'll need to send a "namespace" specific to the resource that will be prepended to every request for that resource. So having it specified at resource level wuold make more sense. It's a need related to haw the WordPress REST api I'm working on is structured, I explained in details here: https://discord.com/channels/837692625737613362/1093124784801468487/1093124784801468487
rival-black
rival-black14mo ago
Currently doesn't support this but it can be a very nice feature, can you create an issue for it? You can also do this namespace setting on the dataProvider side like below, so you have not to pass namespace for all hooks.
const namespaceHandler = (resource: string) => {
switch (resource) {
case "posts":
return `/wp/v2/${resource}`;
case "authors":
return `/v1/${resource}`;
default:
return `/wp/v2/${resource}`;
}
};

export const dataProvider = (apiUrl: string): DataProvider => ({
// ...
getList: async ({ resource }) => {
const url = `${apiUrl}/${namespaceHandler(resource)}`;

const { data, headers } = await axiosInstance.get(url);

const total = +headers["x-total-count"];

return {
data,
total,
};
},
// ...
});
const namespaceHandler = (resource: string) => {
switch (resource) {
case "posts":
return `/wp/v2/${resource}`;
case "authors":
return `/v1/${resource}`;
default:
return `/wp/v2/${resource}`;
}
};

export const dataProvider = (apiUrl: string): DataProvider => ({
// ...
getList: async ({ resource }) => {
const url = `${apiUrl}/${namespaceHandler(resource)}`;

const { data, headers } = await axiosInstance.get(url);

const total = +headers["x-total-count"];

return {
data,
total,
};
},
// ...
});
absent-sapphire
absent-sapphire14mo ago
Sure, I'll go opening an issue later today 👌 Handling the namespace in the dataProvider solves my specific use case, with our specific WordPress installation, but you can't know what plugins (and thus namespaces) you'll find in other WP installation. Thank you very much!
rival-black
rival-black14mo ago
You're welcome 🤞🏻 Btw, you're right if it will be a package. However you can pass namespaces using dataProvider arguments like below:
export const dataProvider = (
apiUrl: string,
namespaces: { resource: string, namespace: string }[]
): DataProvider => ({
// ...
getList: async ({ resource }) => {
const url = `${apiUrl}/${
namespaces.find((n) => n.resource === resource).namespace
}/${resource}`;

const { data, headers } = await axiosInstance.get(url);

const total = +headers["x-total-count"];

return {
data,
total,
};
},
// ...
});

<Refine
...
dataProvider={
dataProvider(
API_URL,
[{resource: "posts", namespace: "wp/w2"}]
)
}
/>
export const dataProvider = (
apiUrl: string,
namespaces: { resource: string, namespace: string }[]
): DataProvider => ({
// ...
getList: async ({ resource }) => {
const url = `${apiUrl}/${
namespaces.find((n) => n.resource === resource).namespace
}/${resource}`;

const { data, headers } = await axiosInstance.get(url);

const total = +headers["x-total-count"];

return {
data,
total,
};
},
// ...
});

<Refine
...
dataProvider={
dataProvider(
API_URL,
[{resource: "posts", namespace: "wp/w2"}]
)
}
/>
absent-sapphire
absent-sapphire14mo ago
that's brilliant! 👍