Skip to content
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

feat: add custom response decoding option #11086

Closed
wants to merge 5 commits into from
Closed

Conversation

jacob-ebey
Copy link
Member

This pull request introduces the feature to define custom response decoding logic through the decodeResponse option. This way, users have the flexibility to parse and manipulate API responses according to their specific needs before the data is further processed or consumed by the application.

The implementation was carried out by adding the decodeResponse option, which can be a function that takes the raw response and returns the decoded data. This feature provides more control over data handling, especially for APIs that return non-standard response structures that require custom parsing logic. It's especially useful in SSR scenarios where you wish to integrate a transport format such as super-json.

Copy link

changeset-bot bot commented Dec 4, 2023

🦋 Changeset detected

Latest commit: 8925423

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 5 packages
Name Type
react-router-dom Minor
react-router Minor
@remix-run/router Minor
react-router-dom-v5-compat Minor
react-router-native Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@@ -36,6 +36,7 @@ import {
} from "react-router";
import type {
BrowserHistory,
DecodeResponseFunction,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be re-exported from react-router-dom?

@@ -4,6 +4,7 @@ import type {
ActionFunctionArgs,
Blocker,
BlockerFunction,
DecodeResponseFunction,
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this need to be re-exported?

@@ -101,6 +101,10 @@ createBrowserRouter(routes, {
<Link to="/" />; // results in <a href="/app/" />
```

## `decodeResponse`

An optional hook to implement custom response decoding logic. This is where you could hook up libraries such as `super-json` or `turbo-stream`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's a Type Declaration section a bit higher up that we should add this too also

@@ -730,6 +730,109 @@ function testDomRouter(
`);
});

it("executes route loaders on navigation with decodeRoute", async () => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
it("executes route loaders on navigation with decodeRoute", async () => {
it("executes route loaders on navigation with decodeResponse", async () => {

Comment on lines +4007 to +4016
let defaultDecode = (): Promise<unknown> => {
let contentType = resultResponse.headers.get("Content-Type");
// Check between word boundaries instead of startsWith() due to the last
// paragraph of https://httpwg.org/specs/rfc9110.html#field.content-type
if (contentType && /\bapplication\/json\b/.test(contentType)) {
return resultResponse.json();
} else {
return resultResponse.text();
}
};
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a strong use-case to provide this defaultDecode to them or is this just so it sort of aligns with what we do in shouldRevalidate? If the latter I'd vote we remove it since the "decoding" we do at the moment is so simple and is nothing more than just standard Web Fetch stuff.

We expose defaultShouldRevalidate for 2 primary reasons:

  • The logic we perform there is RR-specific and a bit nuanced
  • It's possible for them to want to do something custom on their own but then largely use our logic

It feels like decodeResponse doesn't qualify for either of those and is more mutually exclusive. Especially given that defaultDecode will throw if they already read the response body

@@ -741,6 +743,8 @@ export function createRouter(init: RouterInit): Router {
"You must provide a non-empty routes array to createRouter"
);

let decodeResponse = init.decodeResponse;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we get rid of the defaultDecode param, I'd then just lift the defaulting logic up to here and then we don't need to do the option detection each time in callLoaderOrAction:

let decodeResponse = init.decodeResponse || defaultDecodeResponse;

(We'd put defaultDecodeResponse up next to defaultMapRouteProperties)

@brophdawg11
Copy link
Contributor

This is no longer necessary with the dataStrategy implementation - we'll have the ability to do all 4 of the above options via that API alone. Will add unit tests showing those cases in #11098.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants