Title: How to customize data provider request call?
Tags: reactjs;react-admin
Question: I'm new to react and react-admin.
I'm, using jsonServerProvider (in my App.js I have the following):
import jsonServerProvider from 'ra-data-json-server';
I'd like to create a custom bulk action. In a list, select many items and click a button to "connect" them. I tried to use UPDATE_MANY, but this calls my endpoint multiple times, so it's not suitable. Ideally I need the request to call my endpoint like so: url.to.myendpoint?ids=1,2,3 or even better pass an array of IDs in the body and use a PUT request.
Just to understand how things work and debug network calls, I tried also the GET_MANY, in the dataproviders page, the request seems to get the IDs like so: { ids: {mixed[]}, data: {Object} }
But the request is sent to the server like so: url.to.myendpoint?id=1&amp;id=2&amp;id=3 which in my python/flask backend is not nice to parse.
I've spent a bunch of time reading the docs, e.g.:
https://github.com/marmelab/react-admin/blob/master/docs/Actions.md
https://react-admin.com/docs/en/actions.html
https://marmelab.com/react-admin/Actions.html
I tried different approaches and I could not achieve what I want. So again please help me to make my custom bulk button work.
My bulk button is called ConnectItemsButton and the code looks like this:
import React, { Component } from 'react';
import { connect } from 'react-redux';
import { Button, crudUpdateMany } from 'react-admin';
import { showNotification, GET_MANY } from 'react-admin';
import dataProvider from './dataProvider';
class ConnectItemsButton extends Component {
handleClick = () =&gt; {
const { selectedIds } = this.props;
dataProvider(GET_MANY, 'items/connect', { ids: selectedIds })
.then(() =&gt; {
showNotification('Connected!');
})
.catch((e) =&gt; {
showNotification('Error.', 'warning')
});
render() {
return (
<Button label="Associate" onClick={this.handleClick} /&gt;
);
export default connect(undefined, { crudUpdateMany })(ConnectItemsButton);
Note that the contents of ./dataProvider (it's the same provider used in the App.js file and passed to the <Admin&gt; in the props):
import jsonServerProvider from 'ra-data-json-server';
export default jsonServerProvider('http://151.154.11.224:5000/api');
In my list I created it, the button is displayed properly, so here I share the code snippet:
const PostBulkActionButtons = props =&gt; (
<Fragment&gt;
<ConnectItemsButton {...props} /&gt;
</Fragment&gt;
export const ItemsList = props =&gt; (
<List {...props} bulkActionButtons={<PostBulkActionButtons /&gt;}&gt;
In my backend endpoint items/connect I simply need to get a comma separated list of IDs to parse, that's it.
A simple working solution would be awesome, or at least point me in the right direction. Thanks for your help.

Here is the accepted answer: The way I would do this is by using react-admin's dataActions. Your action would be something like this:
crudCreate('items/connect', { selectedIds: selectedIds }, basePath , redirectUrl)
I recommend using a custom dataProvider (e.g. if you use jsonDataProvider, in your App.js import where you see ra-data-json-server: if you use WebStorm Ctrl + click on it and copy the code e.g. to customJsonDataProvider.js and fix eventual warnings, e.g. import lines should be moved at the top) and pass it as props to your Admin component. In your customJsonDataProvider you will have a convertDataRequestToHTTP, or something similar, which manages the CRUD actions and returns the url and HTTP method that you want to use.
An example of what you want to do would be:
const convertDataRequestToHTTP = (type, resource, params) =&gt; {
let url = '';
const options = {};
switch (type) {
...
case CREATE: {
...
if (type === 'items/connect') {
const { data: { selectedIds } } = params;
url = `${apiUrl}?${selectedIds.reduce((acc, id) =&gt; `${acc};${id}`)}`
options.method = 'GET';
}
...
break;
...
return { url, options };
In your dataProvider.js, modify the import to use the custom provider you created, e.g.:
import jsonServerProvider from './customJsonServer';
The code in your AssociateTradesButton.js should work fine as is.
You can find the documentation for creating you own dataProvider here.
Comment for this answer: Hi, I understand that it might seem a bit overwhelming, but it is actually pretty straightforward. In your `/src` folder you just have to add a `dataProvider.js` file and then import it in your `App.js` and pass it as props to the Admin component (``). You have an example for a `dataProvider` in the [tutorial section](https://marmelab.com/react-admin/Tutorial.html#connecting-to-a-real-api) of the react-admin docs. Take a look at [this post](https://blog.theodo.com/2019/04/react-admin-second-api/) too.
Comment for this answer: Hi @firepol, could you mark the solution as the answer if it helped you?
Comment for this answer: Yes, that's a good solution. I also find POST method to be the best way, especially if your list contains a lot of IDs.
Comment for this answer: Thanks. Custom dataProvider you mean in the `switch` I have to re-implement also all the other actions I'm already using like GetList, GetOne, Update etc. or can I just skip them and they work as is? See docu chapter "Writing Your Own Data Provider": is this what you mean to write a custom provider? In the docu the usage looks different, not `crudCreate` but `dataProvider(CREATE,...` maybe can you be more specific please? Code snippet to import & pass the customProvider to the admin would help, I'm a noob in react. Thanks.
Comment for this answer: Also I didn't find anywhere in the docs about `convertDataRequestToHTTP`, can you please share a link with such an example? I googled and the only example I found was here: https://stackoverflow.com/questions/54928428/react-admin-component-does-not-use-dataprovider-to-call-api
Writing a custom data provider just for that one call I need to do different looks like an overkill to me. If there is a way to leave things as is (I already have CRUD functionality working and I'm happy with it) and just to add that one action with minimal code it would be great.
Comment for this answer: ok, but I will edit your answer so that if a noob lands here he will immediately understand what to do, because it was not 100% noob friendly. Instead of creating a custom provider I made a copy of the one I'm using (`jsonDataProvider` to `customJsonServer.js`); got rid of unneeded types (GET, GET_LIST etc.), adapted what you suggested (I did differently, kept "POST" method and sent IDs in the body of the request -which is more elegant- and only modified my `dataProvider.js` file to `import jsonServerProvider from './customJsonServer';` the rest of my code, I left it as is.

Here is another answer: I think using Promise.all will solve the issue, see this link for reference https://github.com/marmelab/react-admin/blob/master/packages/ra-data-simple-rest/src/index.js#L140
Comment for this answer: Thx @Deb for the answer, so if I understand you suggest to make a copy of `ra-data-simple-rest/src/index.js`, then, after line 140 modify e.g. UPDATE_MANY or create my own custom action there? If I create something new e.g. called `CONNECT_ITEMS` can I add it to the ones imported at top or should I rather use an additional if check e.g. `if (type === UPDATE_MANY && ${resource} === 'items/connect')` (or similar)?

{'source': 'StackExchangeDataset', 'subset': 'stackoverflow.com', 'metadata': {'AnswerCount': 2, 'CommentCount': 0, 'ContentLicense': 'CC BY-SA 4.0', 'CreationDate': '2019:00:05/28/19', 'FavoriteCount': 1.0, 'PostTypeId': 1, 'Tags': '<reactjs><react-admin>', 'Title': 'How to customize data provider request call?', 'ViewCount': 3772, 'id': 56349427, 'user_AboutMe': "<p>I'm a self learner IT generalist. Back in high school I learnt HTML &amp; JavaScript. I learnt Linux and got my 1st job as part-time Sysadmin during university, where for fun I learnt PHP &amp; MySQL. Learnt C#, ASP.NET MVC, MS SQL Server on the job. Right now I'm in love with Python and learning React and blockchain related things (blockchain fundamentals and a bit of Solidity language to make smart contracts in Ethereum).</p>\n", 'user_AccountId': 197300.0, 'user_DisplayName': 'firepol', 'user_Reputation': 1671.0, 'user_Views': 186.0, 'user_id': 441301.0}}