Amplify-js: Add the ability to cancel any API call

Created on 6 Jun 2018  Â·  21Comments  Â·  Source: aws-amplify/amplify-js

Do you want to request a feature or report a bug?
feature
What is the current behavior?
an api call can't be canceled once made.

What is the expected behavior?
https://github.com/axios/axios#cancellation

API feature-request

Most helpful comment

Any update on this? We really would like to start using in-flight request cancelation.

All 21 comments

You guys should take look at https://github.com/sindresorhus/ky, it's a wrapper of fetch, and we can just use AbortController.

Any update on this? We really would like to start using in-flight request cancelation.

they say Amplify.API uses axios under the hood for api functions. Axios has the support for in flight call cancellation. Is there any way to tap into that?

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.

Looks like I found this just in time. I am going to take a stab at this, so I don't want the issue to close just yet. Working on a few minor aspects of this now, just deciding on how to proceed with a few opinionated parts, then testing.

Here is what I am thinking about how the implementation should look. I think axios's cancel token should be aliased so that we don't need to import axios to do anything. This is just a single APIcall example. but you could reuse the same token for multiple calls if you wanted to cancel all, or create a source for each call you want to cancel independently.

const { API } = require('aws-amplify');
const cancelTokenSource = API.CancelToken.source();
const init = {
   cancelToken: cancelTokenSource.token,
   //...
};
let response = await API.get(API_NAME, PATH, init);
//...

Then when you wanted to cancel (like when a UI component is unmounting)

cancelTokenSource.cancel("Tired of waiting");

@kbuechl did you make more progress on this?

Was this also intended to work for GraphQL requests meaning await API.graphql(listItems, {limit: 100})?

+1 this would be useful

Yeah I have the code for the rest API ready to go but was having problems building and running the test suite. I set it down and haven't come back to it. I'll pick it back up Monday. I was only looking to do the rest api portion since axios supports it already. I can try and take a look at the graphql portion but no promises

Sounds great @kbuechl, I'm looking to get it working with GraphQL requests. I have some filters and they apply as you click or type and I'm looking to cancel pending requests if more filters are added or removed. Currently race situations can happen where an earlier request finishes after the most recent one and that results in the wrong data being displayed according to the filter.

I was not aware that GraphQL requests are done using a different request library than REST request as GraphQL requests, in the end, is just a POST with more or less the same payload and headers as a REST call.

@houmark Sorry I didn't mean to imply they were different under the covers, just that I wasn't sure without looking into it! I took a peak and the graphql stuff any you are right it all uses the same core functions underneath using axios.

Edit: I will dig into it more, but I think the only thing that would need to change is adding logic to cancel the old request when the graphql request is updated.

@houmark @cor1 I went ahead and updated the graphql stuff as well. It would work similar to this, I also added it into the subscription process the same way as listed below. This would result in an error you need to handle in your subscription similar to the catch block below

import { API } from 'aws-amplify';
import * as queries from './graphql/queries';

let cancelTokenSource;

try {
  cancelTokenSource = API.CancelToken.source();
  const allTodos = await API.graphql(graphqlOperation(queries.listTodos, {limit: 100}, cancelTokenSource.token));
//can also use  await API.graphql({queries.listTodos, {limit: 100}, cancelTokenSource.token});
}
catch (err) {
  if (!API.isCancel(err)) {
     // handle error ...
  }
}

// ... elsewhere if you detect a change 
cancelTokenSource.cancel('reason if you want one');

This is looking great! I hope this PR makes it in really fast, as this is gonna solve a lot of race situations for us... Thanks for your work @kbuechl — I will try to test out your PR, but I'm traveling the next 2-3 days so it may not happen until next week.

does this mean it works now?

@a428tm afraid not, this is an open PR waiting for approval and merge. Hopefully, it'll get some attention soon!

@houmark thanks for the quick response. if you don't mind me bugging you.. there isn't any workaround at the moment, is there? :)

Not really. This should cancel the request, which should save bandwidth and all that, but if you're more concerned about not overwriting data in your UI with a delayed request from a race situation because you have multiple requests at the same time, then you could make some kind of local solution where you keep track of each request you have in-flight and ignore anyone but the very latest one.

@houmark understood!

@kbuechl I tried use code-snippet witch you propose, but got error " Property 'CancelToken' does not exist on type 'APIClass' "
I am using "aws-amplify": "1.1.29" and "aws-amplify-react": "2.3.9". Any ideas?

@ex3emxl this has not been added into the library yet. Once the pull request is finished it will be merged to master, then it will be in the next release after that. You can keep an eye on the pull request (linked above) and also keep an eye on the 'Releases' tab on the repo's page.

yea I believe we need this feature to be included

Was this page helpful?
0 / 5 - 0 ratings