question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Provide an easier way to manage both PUT and PATCH methods

See original GitHub issue

Is your feature request related to a problem? Please describe.

I have a FeathersJS application that allows to define a PUT and a PATCH method handlers per service.

I define a rides service that use both of them: The PUT (update) method for data changes and the PATCH method for quick actions.

As the default react-admin data provider only support one way to update a record (update), I did my own calling the right method according to the used service:

dataProvider.ts
import {
  DataProvider as BaseDataProvider,
  GetListParams,
} from 'react-admin';
import {
  Service,
} from '@kidways/api';
import {
  diff,
} from '../utils';
import { client } from './client';

// Make typescript happy with service keys.
const getClientService = (resource: string): Service<any> => client.service(resource);

type FeathersSort = { [key: string]: 1 | -1; };

const oneResponseFormat = (response: any): any => ({
  data: {
    id: response._id,
    ...response,
  },
});

const manyResponseFormat = (response: any): any => {
  let data;
  if (!response.data) {
    response.total = response.length;
    data = response;
  } else {
    data = response.data;
  }

  response.data = data.map((_item: any) => {
    const item = _item;
    item.id = _item._id;
    return _item;
  });

  return response;
};

const clientFind = (resource: string, { pagination, filter, sort }: GetListParams): any => {
  const resolvedSort: FeathersSort = {};
  resolvedSort[sort.field] = sort.order === 'ASC' ? 1 : -1;

  return getClientService(resource)
    .find({
      query: {
        ...filter,
        $sort: resolvedSort,
        $limit: pagination.perPage,
        $skip: pagination.perPage * (pagination.page - 1),
      },
    })
    .then(manyResponseFormat);
};

const needUpdateResources = [
  // ... another services that need to use the update method.
  'rides',
];

interface DataProvider extends BaseDataProvider {
  patch: BaseDataProvider['update'];
}

const patch: DataProvider['patch'] = (resource, { id, data, previousData }) => getClientService(resource)
  .patch(id, diff(data, previousData))
  .then(oneResponseFormat);

export const dataProvider: DataProvider = {
  getList: (resource, params) => clientFind(resource, params),
  getOne: (resource, { id }) => getClientService(resource).get(id).then(oneResponseFormat),
  getMany: (resource, { ids }) => getClientService(resource).find({
    query: {
      _id: { $in: ids },
    },
  }).then(manyResponseFormat),
  getManyReference: (resource, {
    target, id, filter, ...params
  }) => clientFind(resource, {
    filter: {
      ...filter,
      [target]: id,
    },
    ...params,
  }),
  create: (resource, { data }) => getClientService(resource).create(data).then(oneResponseFormat),
  update: (resource, { id, data, ...rest }) => {
    if (!needUpdateResources.includes(resource)) {
      return patch(resource, {
        id,
        data,
        ...rest,
      });
    }

    return getClientService(resource)
      .update(id, (({ id: _, _id, ...dataRest }) => dataRest)(data))
      .then(oneResponseFormat);
  },
  patch,
  updateMany: () => { throw new Error('not implemented'); },
  delete: (resource, { id }) => getClientService(resource).remove(id).then(oneResponseFormat),
  deleteMany: (resource, { ids }) => Promise
    .all(ids.map((id) => getClientService(resource).remove(id)))
    .then(manyResponseFormat),
};

export default null;

With this provider, the rides service will use the update method by default.

For the quick actions buttons that need the patch method, I did specify it on the useMutation hook provided in react-admin v3:

StartButton.tsx
import React, {
  FC,
} from 'react';
import {
  ButtonProps,
  useMutation,
  useNotify,
  useRefresh,
} from 'react-admin';
import {
  PlayCircleFilledOutlined as StartIcon,
} from '@material-ui/icons';
import {
  WithConfirmationButton,
} from '../../../../components';

export const StartButton: FC<ButtonProps> = ({ record }) => {
  const notify = useNotify();
  const refresh = useRefresh();
  const [start, { loading }] = useMutation(
    {
      resource: 'rides',
      type: 'patch',
      payload: {
        id: record?.id,
        data: { start: true },
      },
    },
    {
      onSuccess: () => {
        notify('Conduite démarrée.');
        refresh();
      },
      onFailure: (error) => {
        notify(`Echec : ${error?.message}`, 'error');
        refresh();
      },
    },
  );

  if (!record) {
    return null;
  }

  return (
    <WithConfirmationButton
      label="Démarrer"
      onConfirm={start}
      disabled={loading}
    >
      <StartIcon />
    </WithConfirmationButton>
  );
};

export default null;

In React Admin v4, we have to replace useMutation by useUpdate.

As the useUpdate calls the dataProvider.update method directly, I don’t have the possibility to specify the patch usage anymore.

I may directly use the dataProvider, but I’ll lost all the useUpdate inner logic based on react-query mutations (Query caching, onSuccess/onError callbacks, the possibility to pass the parameters directly on the callback…).

Describe the solution you’d like

Having a as simple as the v3 way to specify which method of the data provider to call on the useUpdate function:

useUpdate(
  'rides',  
  {
    id: 42,
    data: {
      // ...
    }
  },
  {
      method: 'patch' // 'update'|'patch' or a simply a string.
  }
);

We may also consider to introduce a DataProvider.patch optional method definition and its related usePatch hook.

Describe alternatives you’ve considered

As the dataProvider.update method is called in the middle of the useUpdate hook, the only wat I found so far is to do a complete copy/paste of this hook onto a usePatch hook, just to call dataProvider.patch instead.

It works. However, copying ~400 lines of vendor code just to change one of them is very cumbersome and may lead to upgrade maintenance hell situations.

By the way, if you have a simpler workaround, I’am looking for that! 👍

Additional context

N/A

Issue Analytics

  • State:open
  • Created a year ago
  • Comments:7 (7 by maintainers)

github_iconTop GitHub Comments

1reaction
soullivaneuhcommented, May 22, 2022
1reaction
fzaninottocommented, May 22, 2022

Hi, and thanks for sharing your idea.

Matching CRUD events with HTTP verbs is the job of the data provider. Add your PUT/PATCh logic there, and use either react-admin’s useUpdate (if you put the logic in data provider.update), or react-query’s useMutation (if you put the logic in a custom data provider method).

We won’t support passing a custom HTTP verb to useUpdate. You have the meta parameter for that.

Read more comments on GitHub >

github_iconTop Results From Across the Web

HTTP PUT vs HTTP PATCH in a REST API
Let's start with both a simple and a slightly simple statement. When a client needs to replace an existing Resource entirely, they can...
Read more >
Use of PUT vs PATCH methods in REST API real life ...
Simple : POST creates an item in a collection. PUT replaces an item. PATCH modifies an item. When POSTing, the URL for the...
Read more >
When To Use PATCH vs. PUT in Professional REST APIs
The main difference between PUT and PATCH in REST API is that PUT handles updates by replacing the entire entity, while PATCH only...
Read more >
Difference Between PUT and PATCH Request
PUT HTTP Request: PUT is a method of modifying resources where the client sends data that updates the entire resource. PUT is similar...
Read more >
A better way to implement HTTP PATCH operation in REST ...
To update a resource, we should use PUT. But also you could use PATCH as well. The difference between those two is that...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found