[Network] Load failed. Requests are failing on mobile and safari (Safari on both desktop and mobile, and on mobile chrome)
vholik opened this issue · comments
Describe the bug
The application, built using Next.js version 13.5 and utilizing the App router with URQL following this tutorial is encountering errors specifically on Safari (both desktop and mobile) and Chrome on iPhones.
Network requests are consistently failing when the application attempts to make them. This issue is reproducible on mobile devices, including Safari on both desktop and mobile, as well as Chrome on iPhones.
This is my URQL wrapper:
// Wrapper.tsx
"use client"
export const fetchPolyfill = (...args: any) => {
return fetch(...args).then((response) => {
const newSession = response.headers.get("woocommerce-session");
const existingSession = !isServer
? localStorage.getItem("woo-session")
: null;
if (!isServer && newSession && !existingSession) {
localStorage.setItem("woo-session", newSession);
}
return response;
});
};
export const AppProvider = ({
children,
}: {
children: React.ReactNode;
}) => {
const [client, ssr] = useMemo(() => {
const ssr = ssrExchange();
const client = createClient({
url: process.env.NEXT_PUBLIC_WORDPRESS_API_URL!,
exchanges: [cacheExchange, ssr, fetchExchange],
suspense: true,
fetch: fetchPolyfill,
fetchOptions() {
const session = !isServer ? localStorage.getItem("woo-session") : null;
return {
headers: {
"woocommerce-session": `Session ${session}`,
},
};
},
});
return [client, ssr];
}, []);
return (
<UrqlProvider client={client} ssr={ssr}>
{children}
</UrqlProvider>
);
};
// AddToCartButton.tsx
"use client"
import { useMutation, gql } from "@urql/next";
const ADD_TO_CART = gql`
mutation ADD_TO_CART($input: AddToCartInput!) {
addToCart(input: $input) {
cartItem {
key
product {
node {
id
productId: databaseId
name
description
type
onSale
slug
averageRating
reviewCount
terms {
edges {
node {
id
slug
name
taxonomyName
}
}
}
image {
id
sourceUrl
altText
}
galleryImages {
nodes {
id
sourceUrl
altText
}
}
}
}
variation {
node {
id
variationId: databaseId
name
description
type
onSale
price
regularPrice
salePrice
image {
id
sourceUrl
altText
}
}
attributes {
id
attributeId
name
value
}
}
quantity
total
subtotal
subtotalTax
}
}
}
`;
const addToCartButton = () => {
const [{ fetching: addToCartLoading }, addToCart] = useMutation(ADD_TO_CART);
const add = () => addToCart({ input: productQryInput }).then((result) => {
console.log(result)
});
// Aditional ui goes here...
And here is response in network:
We were trying to change CORS settings on the server and make it so it will handle from every domain but it does not help.
Thanks for help in advance.
Reproduction
Reproduction example is in the description
Urql version
"@urql/next": "^1.1.0",
"urql": "^4.0.6",
Validations
- I can confirm that this is a bug report, and not a feature request, RFC, question, or discussion, for which GitHub Discussions should be used
- Read the docs.
- Follow our Code of Conduct
This is likely a usage issue, and if I had to guess based on “Load Failed” being a very ambiguous error, it's indeed likely to be a CORS issue.
Note that I wouldn't recommend a fetch
wrapper just to add a session header as there's basically three alternatives that are a better practice for this, given that you don't have to actually modify the behaviour of fetch
:
- The
fetchOptions
configuration option onClient
- the
mapExchange
- the
authExchange
from@urql/exchange-auth
(which is likely overkill in this case)
I'm unsure why fetch
is being wrapped here since you're already using the fetchOptions
option, but it's not a good idea to separate and rely on timing there.
To get back to the original issue, since there's no reproduction here I can actually look at, if you check this with a different browser, or check the console in Safari, you'll likely get more information about the CORS issue, since the details of this issue are withheld from the JS runtime error.
If I had to guess Access-Control-Allow-Headers
may be missing or allowing domains as a wildcard in Access-Control-Allow-Origin
isn't possible in your case, i.e. is configured incorrectly on the API.
There's not much advice I can give though straight off this.
I'll leave this open as an issue for now, but will convert this into a discussion, as needed, if we have more of an idea what you're dealing with and the conversation here goes on ✌️
Thanks for the response. I will try to try use your suggestions tomorrow and give my feedback on this.
@kitten I'm using fetch wrapper to access http response in order to get header that is generated on the server.
export const fetchPolyfill = (...args: any) => {
return fetch(...args).then((response) => {
const newSession = response.headers.get("woocommerce-session"); // Get header
const existingSession = !isServer
? localStorage.getItem("woo-session")
: null;
if (!isServer && newSession && !existingSession) {
localStorage.setItem("woo-session", newSession); // Store header
}
return response;
});
};
and then later provide it in the request header.
Could you provide an example how to properly get the response header? Thanks
Fair enough.
Basically, the response headers are restricted, since it's expected that behaviour like this is handled differently, since state in the response headers is a little conflicting with other assumptions in GraphQL.
Basically, I would've expected this to either be handled using cookies or a proper auth mechanism. I'd assume that the session you're creating is ephemeral and cookie-like though. Ultimately, I have no opinions and quarrels about how you'd want to implement thi.
I'd just note that I don't find using random headers on a GraphQL response to be particularly idiomatic if it represents auth-like or other state/inputs. If you do decide to create a fetch
wrapper though I'd keep this separate from fetchOptions
.
To keep this on topic though, I'll convert this to a discussion, since I do still think the original issue is most likely a CORS issue (making this more of a candidate for a QnA thread) ✌️