-
Notifications
You must be signed in to change notification settings - Fork 284
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
[DO NOT MERGE] Single Fetch Experiment #2108
Conversation
We detected some changes in |
Oxygen deployed a preview of your
Learn more about Hydrogen's GitHub integration. |
Unless we rethink this API with Remix middleware, I can think of two solutions for fixing the Customer Account API:
export async function loader({context, response}) {
const {data, errors} = await context.customerAccount.query(QUERY);
- return json(data, {
- headers: {
- 'Set-Cookie': await context.session.commit(),
- }
- });
+ response.headers.append('Set-Cookie', await context.session.commit());
+ return data;
})
export async function loader({context, response}) {
- const {data, errors} = await context.customerAccount.query(QUERY);
- return json(data, {
- headers: {
- 'Set-Cookie': await context.session.commit(),
- }
- });
+ const {data, errors} = await context.customerAccount.query(response, QUERY);
+ return data;
}) One major pro of this approach is it's easy to toggle how the customerAccount abstraction works for single-fetch based on the arguments passed. But at the same time, this feels awkward. Ideally a solution would be just: export async function loader({context, response}) {
const {data, errors} = await context.customerAccount.query(QUERY);
return data;
}) I don't think this is possible without a way to inject the Remix response into the customer account abstraction, which should be possible with middleware. Until then, do we pick one of the solutions above? Or do we hold off adopting single-fetch until middleware also ships (which should be soon as it's apart of Remix 3)? |
Reading the docs on SingleFetch, it sounds like the Perhaps this is some feedback we can give to the Remix team on middleware: it would be nice to have access to these response stubs somehow for each loader/action. I'm not sure if this is possible but I could imagine some kind of AsyncLocalStorage-like utility where we can call However, I think for now we probably need to go the explicit way and assume we are not going to have this functionality in middleware 🤔 So going back to what's possible... I think would prefer signatures like context.customerAccount.query(QUERY, {response}) which is similar to the storefront client and lets us add more options or required params. |
I think this is mostly true from a type perspective where the data returned would be different. However, the part of the utilities we want to access in our utilities are the shared portion that will always be the same. I do agree for now passing in |
0d2d360
to
1d43bda
Compare
@frandiox @michenly part of me thinks it's better to not add an argument for now, and instead force the user to just handle it on the outside. Otherwise we are going to introduce a breaking change right now to require a I want to optimize for the least amount of changes required for developers. Or maybe another option is we wait to adopt single-fetch until there's also a middleware solution. |
@blittle I will take the breaking change into consideration and think about it some more. However... I just finished the refactor for adding an response option for both cart & customerAccount. And I am actually catching A LOT of possible errors we made around when the header setting should occur. Moving it into the utilities is helping to ensure the session setting always occur because we can set the response header when we set session. So I wonder if the breaking change is actually worth it for the quality improvement. |
@michenly I agree that it should be inside our abstraction. It was always my concern that it would be forgotten if left to the user on the outside. I'm just saying I want to minimize API changes. If we make this change now, and then in a few weeks middleware comes out, then we want to change it again? I'd rather find a way with middleware to inject the |
export async function loader({context}: LoaderFunctionArgs) { | ||
return context.customerAccount.authorize(); | ||
} | ||
export const loader = defineLoader(async ({context, response}) => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One thing to note, resource routes (where only a loader is exported, no default export) it's not recommended to use defineLoader
: https://remix.run/docs/en/main/guides/single-fetch#resource-routes
It is not recommended to use defineLoader/defineAction for externally-accessed resource routes that need to return specific Response instances. It's best to just stick with loader/LoaderFunctionArgs for these cases.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's confusing...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
super confusing indeed...
So the reason why I changed it to defineLoader is because the type for response in loader is ResponseStub | undefined
., and I want to ensure response is always defined.
Switching back to loader
means I likely need to handle the case where ResponseStub is undefined. Or I can just assume ResponseStub is always defined. Which means this route is always being hit with Remix APIs (It actually is, since it is being redirected). But at this point, I can also assume this is NOT a true Resource Route that would cause type error thou.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In general I feel like loaders are more confusing with single fetch. Resource route loaders still return new Response
, other loaders don't :(
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems like it doesn't matter if we wrap it in defineLoader
, so maybe we just keep it consistent even if they say it's not recommended? 🤷🏼
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going with loader
unless the type fail and I specifically needed defineLoader
so far. Will see if anything fail thou. I am testing CA API right now and finding some interesting failure. Fixing it while testing to see slowly what the pattern is.
@michenly @blittle Hey here's another random idea: We already have both Or maybe we don't do this automatically but we at least warn the users here if they didn't call |
87f3162
to
3acc277
Compare
29106f2
to
2aa7073
Compare
55068c7
to
a43e7c4
Compare
e317832
to
4a9f98f
Compare
remove any defer & json loader or action return remove all of redirect not sure how to solve these ... replace UIMatch remove deprecate methods from remix-oxygen change new Response in loader/action to use response arg instead remove question mark from response when it is coming from defineLoader/defineAction, also use headers.set when its right before the return fix CSP with Inline Scripts fix search type with loader type update cart utility and put response into options update customerAccount client to use response to set session header fix redirect in loader (resource route), ensure to return a Response instead of manipulating the response object get positive flow working. Looks like action & loader works slightly differently run codemod on skeleton resource route, return response object instead add nativeFetch fully use ResponseStub get rid of session.helper revert to response object return revert Hydrogen package use Response Object for utilities and ResponseStub for skeleton template add cf compat flag
refactor to handle when root data didnt load (fix type later) remove RootLoaderData and typed using generated query/fragment type instead update all the examples as well
WHY are these changes introduced?
This PR aims to try out Remix's Single Fetch feature ahead of v3
Close https://github.com/Shopify/hydrogen-internal/issues/108
WHAT is this pull request doing?
Must do
<RemixServer />
http_headers_getsetcookie
=> need to make this configurableremove the optional changes for v2 into branch mc-single-fetch-v3-template
Template Update (optional for Remix v2 user with future flag on) * use ResponeStub instead of Response Object everywhere * Any response body for error route can now throw new Error * replace `redirect` with ResponeStub (throw right away) and always use headers.set for locations * Watch out for redirect that occur in parent and child route. Parent will likely take precedent * remove `defer` everywhere * remove `json` everywhere, unless you specifically want to serialized the response * Type improvements: UIMatch_SingleFetch, MetaArgs_SingleFetch * Use response!.status syntax in loader/action even thou the type of response can be undefinedHOW to test your changes?
Post-merge steps
Checklist