redux-toolkit
redux-toolkit copied to clipboard
Return type of transformResponse on enhanceEndpoints can not differ from original definition in createApi
Currently, the return type of transformResponse on enhanceEndpoints has to match the original return type from createApi.
This diminishes the value and ability of enhanceEndpoints:
Often, it is preferred to have client code use different shapes than the ones exposed by an API and transformResponse is the mechanism to do so.
However, once RTK codegen is used, transformResponse should be defined on enhanceEndpoints and not on the (generated) createApi.
Example
Using the enhancedEndpoints example from the docs, this usage of transformResponse currently fails to compile:
getPetById: {
providesTags: (result, error, arg) => [{type: 'Pet', id: arg.petId}],
transformResponse: ({id, name, category, photoUrls, tags, status}) => ({id, name}),
},
I just ran into this myself. A bit annoying.
However, I think I understand why this limitation might exist.
Disclaimer: My team uses React hooks, so I don't know much about how enhanceEndpoints might be used elsewhere.
But what I do know, is that enhanceEndpoints does not create new hooks.
The same hooks that get returned from createApi and/or injectEndpoints, are the exact same hooks returned by enhanceEndpoints (tested with ===).
e.g. if a React component imports a useQuery hook from the createApi call, and a different module later calls enhanceEndpoints, the existing useQuery hook will be affected.
Therefore, changing the return type of transformResponse would require that the types within the existing hooks get updated as well, which is impossible especially across modules.
The only way that a change of return type would be realistically possible, would be if enhanceEndpoints just returned a whole new api object with new, separate hooks and whatnot.
But that sounds really complicated and potentially confusing to use.
It is theoretically possible, but depending on what file you import from you will get things of different types - and enhanceEndpoints does not create a new instance, but changes the original instance, but cannot change it's types, so you'd have multiple versions of the same api with the same (enhanced) behaviour, but very different types.
So we kinda stayed away from it for now. But I understand the use and I will take another look into it, given the time.
(Thanks for rtkq, I'm transitioning some code to it now and it's amazing.)
I don't know if one of the things that's holding this up is lack of ideas for how to make it ergonomic, but in case that is the issue I have an idea that might work as a jumping off point for brainstorming.
In the OpenAPI context -- which is where I think of this coming up the most -- would it be possible to handle this by treating the enhancing api as an intermediate phase specified in config, changing the way that code generation ends up?
What I'm imagining is that where right now the outputFile contains the result of injectEndpoints its types, and all the hook exports, you could just generate another file that has all the modified types and use statements.
I can imagine that if a user specifies a trio of new enhance* fields it might be possible to make make this work pretty well.
As a walkthrough of the simple case of only one input file:
Where right now you specify:
const config = {
schemaFile: "...",
apiFile: "empty.ts",
apiImport: "empty",
outputFile: "api.ts",
}
You could add enhanceInputFile, enhanceFile, enhanceImport:
const config = {
schemaFile: "...",
apiFile: "empty.ts",
apiImport: "empty",
enhanceInputFile: "middle.ts", // <-- new
enhanceInputExport: "middle", // <-- new
enhanceFile: "enhance.ts", // <-- new
enhanceImport: "enhance", // <-- new
outputFile: "api.ts"
}
The openapi-codegen program then generates the enhanceInputFile looks exactly like the current outputFile except it does not include any of the use* reexports. This is mostly just to make it more clear that it should not be used by itself.
After that, the enhanceFile contains either an middle.enhanceEndpoints or some new api that returns a new object. This file is written by humans who would like to provide transforms/etc.
Then, the openapi codegen reads this new file, does magic with types -- or just requires that all types be defined explicitly in the middle file -- and outputs basically a duplicate of the inhanceInputFile but with fully correct types.
Does that seem... possible?
It is theoretically possible, but depending on what file you import from you will get things of different types - and enhanceEndpoints does not create a new instance, but changes the original instance, but cannot change it's types, so you'd have multiple versions of the same api with the same (enhanced) behaviour, but very different types.
That's no problem for me personally, as I'm only going to import from the enhancedApi file (and maybe even enforce it with eslint or something)
Anyway, you still change the api instance type in terms of tags, so I don't see why not in terms of return types https://github.com/reduxjs/redux-toolkit/blob/master/packages/toolkit/src/query/apiTypes.ts#L110
If you want to open a PR to it, I'd be open for it. I just don't have the time to work on that at the moment.
+1
I am using codegen for GraphQL queries and would like transform the result.