-
-
Notifications
You must be signed in to change notification settings - Fork 51
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Global onSuccess
interceptor fires AFTER action getData
?
#397
Comments
Here you can see the response flow https://github.com/klis87/redux-requests/blob/master/packages/redux-requests/src/middleware/create-send-requests-middleware.js#L294 I have plans to make a chart showing all of this, and what happens when caching, ssr rehydration and so on You are right, getData is called before and really it probably should be different. Actually I am thinking about deprecating getData, you can achieve the very same thing with meta.onSuccess, so getData is redundant and duplicating onSuccess in API. Regarding onResponse I am going to add it, so it will be possible to change success into error. For now you could achieve onResponse with just a driver, whether you would copy the one you use and update it to your need, or wrap a driver like axiosDriver and add chained You touch actually a very difficult part of design in this lib, there are several issues I will need to tackle which are not ideal now:
|
Every time you think you've made progress, some code monkey pops up and makes your life miserable ;-) But anyway, thanks for the driver tip, I'll think about it, either go with driver or use http codes, i haven't quite decided yet on the final API so it's still easy to change. Given how many big API's don't use HTTP codes for their errors, it might make sense to go with the error key, but at the same time, http errors are a tried and true method... getData Interesting point about getData, however, it might not be a bad idea to keep it, with Interceptors As for interceptors for cached queries, well, that's also interesting, I think that should always receive the original data, but maybe your dilemma is if the interceptors should be called again for cached queries? I think probably interceptors don't need to be called for cached queries... but it's a tough one. interceptor return type Yeah, returning an error rather than throwing it would be more intuitive. In general I think there is a challenge with error checking. I for example have a pretty wide error checker that checks both HTTP error codes and But in general I think you've made huge progress with this library and it mostly does what it has to do! |
Re getData thx for your thoughts, indeed I also have mixed feelings about it Re interceptors and cached queries, the issue is that they should be called for side effects, but not for data transformation :) And they cannot get raw data, because I store only transformed data. I do it on purpose, because otherwise people doing mutations would need to mutate both raw and transformed data, which would be terrible. Transformed data has to be stored and can be the only one source of truth, otherwise I won't be able to have normalisation, caching, ssr, mutations working together, at least I dont know how otherwise. And caching reuses just data stored, cached queries doesnt have separate information, only that a query is cached and a timeout, which then makes dispatch(cachedRequest) not firing new request but getting data back from store. I have some stuff to do before, but once I have a design plan for new interceptors, I will ping you so you might add some thoughts and worries before releasing the new version. |
@zeraien I though about it and possibly I found the solution for all problems, see design for new interceptors:
|
atually I think that then it would be possible just to return |
I didn't managed to dive deep into all pros and cons mentioned here (lack of free time), but at a first glance I think it's better to not implement global onSuccess interceptors or whatever handler to separate error and success responses because from my point of view responsibility to properly parse response and throw or not to throw error lies with Driver. The reason is that, for example, slack API is not truly REST-based because it doesn't follow all of REST rules (at least server can send error responses with status 200, which goes against the documentation), and this means that REST driver (fetch or axios) can't properly handle such responses, and from my point of view to handle that API separate driver should be implemented. This driver should be build on top of some REST driver, and do additional handling of responses with status 200 ( In addition beside standard REST API and those API's that you've mentioned (like slack API etc.) there is many other protocols that never uses any of standard HTTP error responses, and includes information about possible error in response body. Examples of this protocols: JSON-RPC, XML-RPC, SOAP, GraphQL (which is implemented in this library via separate driver, by the way). So, despite global onSuccess interceptor will be handy for end users of this library (especially for those who doesn't know nothing about API of drivers) I think such interceptor will be a bad design decision. As a possible alternative - may be it will be good to add onResponse interceptor for axios and fetch drivers. This way it will be easier for end users to improve default HTTP behavior based on the needs, e.g. something like this: import { createDriver as createAxiosDriver } from '@redux-requests/axios';
import axios from 'axios';
const axiosDriver = createAxiosDriver(axios);
const { requestsReducer, requestsMiddleware } = handleRequests({
driver: {
rest: axiosDriver,
// Possible implementation with 'onResponse' interceptor on diver-level:
slack: createAxiosDriver(axios, {
onResponse(response) {
if (response.status === 'ok') return response.object;
else throw new SlackAPIError(response);
}
}),
// Possible implementation with current architecture (on top of axios driver):
slack: (requestConfig, requestAction, driverActions) => {
return axiosDriver(requestConfig, requestAction, driverActions).then(response => {
if (response.status === 'ok') return response.object;
else throw new SlackAPIError(response);
});
}
}
}) |
@avasuro yeah, agreed, examples you mention belong to drivers, and, regarding slack, I am really surprised they did it like this, and indeed status 200 will be treated as a success response. Anyway, the responsibility you mention is about distinguishing errors and successes on protocol level, nice ideas to chain axios driver promise to transform it into another, nice functional technique in practice! However, there is yet another level, the ap level, like people could want to catch errors with expired token and so on, that shouldn't be part of driver. For this, we need interceptors. Also, one could have a global logic for all responses, no matter what driver used. The biggest change I am going to make is transforming |
I noticed that if you put
onSuccess
interceptor inhandleRequests
, it is always fired after thegetData
method of each individual action.But consider this use case:
Many API's include a "success" key in their responses, rather than using HTTP error codes (Slack for example uses an
ok
key), so you might want to have a central place to process the API response and look forok
orsuccess
key, and remove them from the equation... For example.Consider this response:
So in your FETCH action you might use
getData
:But this also means that your
onSuccess
call no longer sees theok
key at all, you just see the result of thegetData
call...I'm not sure if this is desired behavior or a bug, but I somehow feel that central interceptors should get the fresh data first and the actions get the data last... What do you think?
I am aware that we might just do processing of the response in the
onSuccess
interceptor, but that will create inconsistent results of the actionsgetData
call sometimes touches the response data. The best case scenario is that the global interceptor homogenizes the response so that all actions always know what to expect if they need togetData
...I suppose the easiest solution to this might be to have an
onResponse
global interceptor? Since we also have no way of labeling a request as an error fromonSuccess
...The text was updated successfully, but these errors were encountered: