wretch icon indicating copy to clipboard operation
wretch copied to clipboard

Retry Middleware

Open opheus2 opened this issue 1 year ago • 4 comments

Hey guys 👋🏿 I'm having issues with the retry middleware.

How can I retry a request only when it's a fetchError only and not for all requests?

I tried the retryOnNetworkError as true and false and noticed it sends a retry for non fetchError requests.


import wretch from "wretch"
import fetch from 'node-fetch';
import { retry } from "wretch/middlewares";

global.fetch = fetch;

wretch()
    .polyfills({ fetch: global.fetch })
    .middlewares([
        retry({
            maxAttempts: 4, // Maximum number of retry attempts
            retryOnNetworkError: true, 
            resolveWithLatestResponse: true, // Resolve with the latest response
            onRetry: ({ error, options, response, url }) => {
                if (error) {
                    console.log(`Retrying attempt ${error.attempt} for ${url}`);
                } else {
                    console.log(`No error object present for ${url}, review if retry is necessary.`);
                }
            },
            skip: (url, opts) => {
                console.log(`url: ${url}`);
                return false;
            }
        })
    ]);

// usage
wretch(API_URI)
        .defer((w, url, options) => {
            if (options.context?.token) {
                return w.auth(`Bearer ${options.context.token}`);
            }

            return w
        })
        .resolve(resolver =>
            // Validation error
            resolver.error(422, err => {
                let error = null;

                try {
                    let json = JSON.parse(err.text);

                    let errMsg = _.flatten(_.map(json.errors, _.values))

                    error = `${json.message} ${errMsg}`;
                } catch (e) {
                    error = `Sorry, please try again. 422 Unprocessable Entity.`;
                }

                throwApiError(error);
            })
                // Unauthenticated
                .error(401, err => {
                    throwApiError('Sorry, please try again. 401 Unauthenticated.');
                })
                // Unauthorized
                .error(403, err => {
                    throwApiError('Sorry, please try again. 403 Unauthorized.');
                })
                // Payload Too Large
                .error(413, err => {
                    throwApiError('Sorry, please try again. 413 Payload Too Large.');
                })
                // Too many requests
                .error(429, err => {
                    throwApiError('Sorry, please try again. 429 Too many requests.');
                })
                // Client Closed Request
                .error(499, err => {
                    throwApiError('Sorry, please try again. 499 Client Closed Request.');
                })
                // Server error
                .error(500, err => {
                    throwApiError('Sorry, please try again. 500 Server error.');
                })
                // Bad Gateway
                .error(502, err => {
                    throwApiError('Sorry, please try again. 502 Bad Gateway.')
                })
                // Generic fetch error
                .fetchError(err => {
                    console.log('got here');
                    console.log(err);

                    throwApiError('Sorry, please try again. Fetch error.');
                })

opheus2 avatar Feb 07 '24 17:02 opheus2

Hey @opheus2,

Did you try using the until option?

You can pass it to the middleware like this:

retry({
  // Replace the default condition with a custom one:
  until: (response, error) => !error
})

elbywan avatar Feb 07 '24 20:02 elbywan

Sadly that doesn't do the trick either @elbywan it keeps sending a request even if it is not failed.

opheus2 avatar Feb 07 '24 22:02 opheus2

perhaps we can add a flag to make it work only for network errors.

retryOnlyOnNetworkError: true

opheus2 avatar Feb 07 '24 23:02 opheus2

Sadly that doesn't do the trick either @elbywan it keeps sending a request even if it is not failed.

@opheus2 Hmm weird it seems to be working on my end:

import wretch from "wretch"
import { retry } from "wretch/middlewares";

const api = wretch("https://httpstat.us")
  .middlewares([
    retry({
      maxAttempts: 4, // Maximum number of retry attempts
      retryOnNetworkError: true,
      resolveWithLatestResponse: true, // Resolve with the latest response
      until: (response, error) => !error,
      onRetry: ({ error, options, response, url }) => {
        if (error) {
          console.log(`Retrying because of error "${error.message}" for ${url}`);
        } else {
          console.log(`No error object present for ${url}, review if retry is necessary.`);
        }
      },
      skip: (url, opts) => {
        console.log(`url: ${url}`);
        return false;
      }
    })
  ])
  .resolve(resolver =>
    resolver
      .badRequest(err => {
        console.log('bad request');
        console.log(err.message);
      })
      // Generic fetch error
      .fetchError(err => {
        console.log('fetch error');
        console.log(err.message);
      }))

api.get("/400").text(console.log).catch(error => console.log(error.message))
url: https://httpstat.us/400
bad request
400 Bad Request
url: https://httpstatsssssssss.us/400
Retrying because of error "fetch failed" for https://httpstatsssssssss.us/400
Retrying because of error "fetch failed" for https://httpstatsssssssss.us/400
Retrying because of error "fetch failed" for https://httpstatsssssssss.us/400
Retrying because of error "fetch failed" for https://httpstatsssssssss.us/400
fetch error
fetch failed

elbywan avatar Feb 09 '24 20:02 elbywan

@opheus2 any news? Did I miss something?

elbywan avatar Feb 19 '24 07:02 elbywan

Hi @elbywan

I have now tested this and can confirm yes this approach is working. I my previous test after your first suggestion revolved around my solution using then until: (response, error) => !error, without the respect part of the middleware, hence the weird behaviour. Thank you and I'd close this now

opheus2 avatar Feb 19 '24 09:02 opheus2