agraboso / redux-api-middleware

Redux middleware for calling an API.
MIT License
1.49k stars 202 forks source link

How to detect timeout or refused connection? #135

Open karladler opened 7 years ago

karladler commented 7 years ago

I have something like this:

//...
    return {
        [CALL_API]: {
            endpoint,
            method,
            types: [
                types.API_POST_CREDENTIALS_REQUEST,
                {
                    type: types.API_POST_CREDENTIALS_SUCCESS,
                    payload: (action, state, res) => apiUpdateSuccess(action, state, res)
                },
                {
                    type: types.API_POST_CREDENTIALS_FAILURE,
                    payload: (action, state, res) => apiUpdateFailure(action, state, res)
                }
            ],
            headers: {'Content-Type': 'application/json'},
            body: JSON.stringify(payload)
        }
    };
//...

function apiUpdateSuccess(action, state, res) {
    return getJSON(res).then((data) => {
        return data;
    });
}

function apiUpdateFailure(action, state, res) {
    return getJSON(res).then((data) => {
        return data;
    });
}

But when the API is offline or a request times out, neither apiUpdateSuccess nor apiUpdateFailure is fired. What would be the best practice to detect timeout and offline API?

nason commented 7 years ago

Hi @abimelex,

The Lifecycle docs describe that errors at the request phase will dispatch a REQUEST action (in your case types.API_POST_CREDENTIALS_REQUEST) with an error payload. Can you confirm if that is happening?

maryam-mokhtari commented 6 years ago

I have the exact problem, it reaches the REQUEST part, but what if it times out?

Artforge commented 6 years ago

similar issue here - when a request fails in a manner that it doesn't return (CORS) access headers, the Failure action is never called - a timeout would definitely help here.

arash-a2k commented 6 years ago

I think this is a very important feature to have. We also have the problem of catching timeout for requests on the frontend side, if the server does not send a timeout response.

karladler commented 6 years ago

I think the issue is caused by the nature of fetch not throwing an error on timeout. But maybe it can be implemented into the middleware? see: https://stackoverflow.com/a/49857905/1059828

marcramser commented 5 years ago

I wonder if there have been any progress? Or does anyone have a working "not so hacky" workaround?

nason commented 5 years ago

The fetch spec does not mention "timeout" at all. See https://github.com/matthew-andrews/isomorphic-fetch/issues/48 for more context.

I understand this "expected behavior" leaves a lot to be desired. We've added the ability to pass in your own fetch wrapper at the RSAA level (v2.x -- https://github.com/agraboso/redux-api-middleware#rsaafetch), and soon at the middleware level (v3.x, in beta -- https://github.com/agraboso/redux-api-middleware/tree/next#createmiddlewareoptions)

With that, you could use something like https://www.npmjs.com/package/whatwg-fetch-timeout or https://davidwalsh.name/fetch-timeout as your fetch wrapper. Any extra fields passed into [RSAA].options will be passed to fetch, so you should be able to pass it a timeout.

Does this help at all? This is entirely untested just putting my thoughts here. Would love to see this documented!

WarrenBuffering commented 5 years ago

Any updates on this? I've tried adding the options field with a timeout but it still doesn't trigger, even if I try setting it for a very small window like 500ms

export const fetchDefaultFeeds = () => ({
    [RSAA]: {
        endpoint: `${API_URL}${endpoint}${defaultEndpoint}`,
        method: 'GET',
        headers: {'Content-Type': 'application/json' },
        options: { timeout: 500 },
        types: [
            ActionTypes.FETCH_DEFAULT_FEEDS_START, 
            ActionTypes.FETCH_DEFAULT_FEEDS_SUCCESS, 
            ActionTypes.FETCH_DEFAULT_FEEDS_FAIL
        ]
    }
})