rayman1104 / ra-data-nestjsx-crud

Data provider which integrates React Admin with NestJS CRUD library
MIT License
100 stars 27 forks source link

Request: a way to pass in custom data parsers #7

Closed aditibarbhai closed 3 years ago

aditibarbhai commented 3 years ago

Great package! But would love the ability to pass in a custom data parser/mapper for the data that is returned in the response.

(See this: https://github.com/marmelab/react-admin/blob/master/docs/FAQ.md#can-i-have-custom-identifiersprimary-keys-for-my-resources)

You could have something like this:

return httpClient(url).then(({ json }) => ({
            data: options.customDataParser ? options.customDataParser(json.data) : json.data,
            total: json.total,
        }));
rayman1104 commented 3 years ago

Hi! I'm not sure if we need that inside the package. Maybe you have an example of how it's implemented in other react-admin data providers? The idea of react-admin providers is that you can extend them easily, see Extending a Data Provider in the docs. Here is how it can look in your code:

import dataProvider from 'ra-data-nestjsx-crud';

export const myDataProvider = (apiUrl: string, httpClient = fetchUtils.fetchJson) => {
  const provider: DataProvider = dataProvider(apiUrl, httpClient);
  return {
    ...provider,
    getList: (resource, params) => {
      return provider.getList(resource, params).then(({ data, total }) => ({
        data: myCustomDataParser(data),
        total,
      }));
    },
  };
}
aditibarbhai commented 3 years ago

getList: (resource, params) => { return provider.getList(resource, params).then(({ data, total }) => ({ data: myCustomDataParser(data), total, })); },

Hi @rayman1104 - thanks for your response. I tried the approach you suggested but I get a TypeScript error:

Type '{ getList: (resource: any, params: any) => Promise<{ data: { id: any; }[]; total: number; }>; getOne: (resource: any, params: any) => Promise<{ data: any; }>; getMany: <RecordType extends Record = Record>(resource: string, params: GetManyParams) => Promise<...>; ... 5 more ...; deleteMany: (resource: string, params:...' is not assignable to type 'DataProvider | LegacyDataProvider'.
  Type '{ getList: (resource: any, params: any) => Promise<{ data: { id: any; }[]; total: number; }>; getOne: (resource: any, params: any) => Promise<{ data: any; }>; getMany: <RecordType extends Record = Record>(resource: string, params: GetManyParams) => Promise<...>; ... 5 more ...; deleteMany: (resource: string, params:...' is not assignable to type 'DataProvider'.
    The types returned by 'getList(...)' are incompatible between these types.
      Type 'Promise<{ data: { id: any; }[]; total: number; }>' is not assignable to type 'Promise<GetListResult<RecordType>>'.
        Type '{ data: { id: any; }[]; total: number; }' is not assignable to type 'GetListResult<RecordType>'.
          Types of property 'data' are incompatible.
            Type '{ id: any; }[]' is not assignable to type 'RecordType[]'.
              Type '{ id: any; }' is not assignable to type 'RecordType'.
                '{ id: any; }' is assignable to the constraint of type 'RecordType', but 'RecordType' could be instantiated with a different subtype of constraint 'Record'.  TS2322
rayman1104 commented 3 years ago

Types of property 'data' are incompatible.

It says that your custom data parser return type is not compatible with what's expected there.

aditibarbhai commented 3 years ago

Yeah I wasn't sure why that was happening because I was using a Record type, but setting GetListResult<any> as the return type ended up working for me:

getList: (resource, params) => {
      return Promise.resolve(provider.getList(resource, params)).then(({ data, total }): GetListResult<any> => {
        return {
          data: customDataParser(data),
          total,
        };
      });
    },