App reviews, troubleshooting, and recommendations
We're moving the community! Starting July 7, the current community will be read-only for approx. 2 weeks. You can browse content, but posting will be temporarily unavailable. Learn more
Hi Developer,
I would really appreciate it if I could get your help regarding my concern.
I’m encountering a CORS error when making a fetch request from my Checkout UI Extension to my app proxy endpoint (/apps/pobox).
Below is my extension toml configuration:
[extensions.capabilities]
block_progress = true
api_access = true
network_access = true
Below is my app.proxy.tsx:
import { json, LoaderFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import prisma from "../db.server"; // Import Prisma client
export const loader = async ({ request }: LoaderFunctionArgs) => {
// Authenticate the request as a public app proxy
await authenticate.public.appProxy(request);
// Fetch the first record from the `poboxToggle` table
const poboxToggle = await prisma.poboxToggle.findFirst();
console.log("Loader function: Toggle status fetched"); // Less verbose log
// Return the fetched record or a default object if no record is found
return json(poboxToggle || { active: false });
};
Below is the output for this proxy, proxy has been setup correctly:
However, whenever I try to fetch this proxy data in Checkout extension, i get CORS error, below is my code in Checkout.jsx:
const { address1 } = useShippingAddress() || {};
const [isPOBoxBlockingActive, setIsPOBoxBlockingActive] = useState(false);
const [fetchError, setFetchError] = useState(null);
const extensionApi = useExtensionApi(); // Get the full object
const shop = extensionApi.shop.myshopifyDomain; // Use the `myshopifyDomain` property
useEffect(() => {
const fetchToggleState = async () => {
try {
const apiUrl = `https://${shop}/apps/pobox`; // Use the shop domain dynamically
console.log("Fetching from:", apiUrl); // Log the fetch URL
const response = await fetch(apiUrl, {
headers: { Accept: "application/json" },
});
console.log("Fetch response:", response); // Log the response object
if (!response.ok) throw new Error(`HTTP error! Status: ${response.status}`);
const data = await response.json();
console.log("Toggle state fetched successfully:", data);
setIsPOBoxBlockingActive(data.active);
setFetchError(null);
} catch (error) {
console.error("Failed to fetch toggle status:", error);
setFetchError("Failed to load PO Box settings. Please try again later.");
}
};
fetchToggleState();
}, [shop]); // Add shop to the dependency array
However, I am still getting below error message in console log:
Thank you for your time.
Many thanks,
Hey @Kit_ ,
Thank you for reaching out regarding the CORS error you're encountering in your Checkout UI Extension. I understand how critical it is to ensure seamless functionality in your application, and I’m here to help you resolve this issue.
Root Cause of the Issue:
The CORS error occurs because browser-based requests from your Checkout UI Extension to the app proxy endpoint are blocked by the browser for security reasons. This happens when the app proxy endpoint does not include the appropriate Access-Control-Allow-Origin header in its response.
Solution:
To resolve this, you need to ensure your app proxy endpoint (/apps/pobox) includes the required CORS headers in its response. Below are the steps to fix this:
Update Your app.proxy.tsx:
Add the necessary CORS headers to the response. Here’s how you can modify your loader function:
import { json, LoaderFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import prisma from "../db.server";
export const loader = async ({ request }: LoaderFunctionArgs) => {
// Authenticate the request as a public app proxy
await authenticate.public.appProxy(request);
// Fetch the first record from the `poboxToggle` table
const poboxToggle = await prisma.poboxToggle.findFirst();
console.log("Loader function: Toggle status fetched");
// Set CORS headers
const headers = {
"Access-Control-Allow-Origin": "*", // Allow requests from all origins
"Access-Control-Allow-Methods": "GET, POST, OPTIONS",
"Access-Control-Allow-Headers": "Content-Type, Authorization",
};
// Return the fetched record or a default object if no record is found
return new Response(
JSON.stringify(poboxToggle || { active: false }),
{
status: 200,
headers: {
...headers,
"Content-Type": "application/json",
},
}
);
};
Clear your browser cache to ensure no old CORS issues persist and test the fetch request in the Checkout UI Extension. Monitor the browser console for any further errors.
Please feel free to reach out via email, so we can discuss your requirements in more detail. I would be happy to help you resolve this and ensure everything works as expected.
Looking forward to hearing from you soon!
Best regards,
Rajat
Hi @rajweb ,
Thank you for your help and information.
I did make change as per your post as per below:
import { json, LoaderFunctionArgs } from "@remix-run/node";
import { authenticate } from "../shopify.server";
import prisma from "../db.server"; // Import Prisma client
export const loader = async ({ request }: LoaderFunctionArgs) => {
// Authenticate the request as a public app proxy
await authenticate.public.appProxy(request);
// Set CORS headers
const headers = new Headers({
"Access-Control-Allow-Origin": '*',
"Access-Control-Allow-Methods": 'GET, POST, OPTIONS',
"Access-Control-Allow-Headers": 'Content-Type, Authorization, Content-Type, Accept, Authorization, X-Requested-With, Application, ip',
"Content-Type": "application/json"
});
// Handle OPTIONS method for CORS preflight
if (request.method === 'OPTIONS') {
return new Response('', {
status: 204,
headers: headers
});
}
// Fetch the first record from the `poboxToggle` table
const poboxToggle = await prisma.poboxToggle.findFirst();
console.log("Loader function: Toggle status fetched");
// Return the fetched record or a default object if no record is found
return new Response(
JSON.stringify(poboxToggle || { active: false }),
{
status: 200,
headers: headers
}
);
};
Below is the output for proxy url and displaying correctly:
However, i am still get the same CORS issue for the below code in Checkout.jsx extension:
useEffect(() => {
const apiUrl = 'https://store-name.myshopify.com/apps/pobox'; // API URL
fetch(apiUrl, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
}
})
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! Status: ${response.status}`);
}
return response.json();
})
.then(data => {
setIsPOBoxBlockingActive(data.active); // Assuming 'active' is a boolean
})
.catch(error => {
console.error("Failed to fetch toggle status:", error);
setFetchError("Failed to load PO Box settings. Please try again later.");
});
}, []); // Only run once when component mounts
Below is the CORS message:
I also faced the same problem, When I upgrade the shopify and remix version, did you find a solution?
Have you found a solution for this? I had a solution that worked for me but suddenly it stopped working not sure why and I have been struggling to fix it.
This is the solution that USED to work:
export const loader: LoaderFunction = async ({ request }) => {
const { cors, sessionToken } = await authenticate.public.checkout(request)
return cors(json({ message: "success" }))
}
CHECKOUT EXTENSION:
const { query, sessionToken } = useApi()
const token = await sessionToken.get()
const response = await fetch(`${APP_URL}/api/`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({}),
})
I have not able to find the solution?
Please let me know if have any solution to fetch data in Checkout extension.
Hey I found the solution all you have to do is add ‘cors: true’ to your vite.config. It was that simple and I spend like 3 days searching for that. The code in my previous reply works.
export default defineConfig({
server: {
port: Number(process.env.PORT || 3000),
hmr: hmrConfig,
fs: {
// See https://vitejs.dev/config/server-options.html#server-fs-allow for more information
allow: [
"app",
"node_modules”
],
},
cors: true, // this is what you need to add
},
plugins: []
}) satisfies UserConfig
Thank you @manuelpineda , it is working and we had to pass data via action function inorder to fetch data from checkout extension, below was my code:
app.api.tsx:
import { LoaderFunction, ActionFunction } from "@remix-run/node";
import { json } from "@remix-run/node";
import { authenticate } from "../shopify.server";
// This loader is optional if you want to support GET requests too
export const loader: LoaderFunction = async ({ request }) => {
const { cors } = await authenticate.public.checkout(request);
return cors(json({ message: "GET success" }));
};
// The action function will handle POST requests.
export const action: ActionFunction = async ({ request }) => {
const { cors } = await authenticate.public.checkout(request);
return cors(json({ message: "success" }));
};
Below is my Checkout.jsx:
const { sessionToken } = useApi();
const [message, setMessage] = useState("Loading...");
useEffect(() => {
async function fetchData() {
try {
const token = await sessionToken.get();
console.log("Session token:", token);
const response = await fetch(`${APP_URL}/app/api/`, {
method: "POST",
headers: {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
Accept: "application/json",
},
body: JSON.stringify({}),
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log("Response data:", data);
setMessage(data.message);
} catch (error) {
console.error("Error fetching API:", error);
setMessage("Error fetching data.");
}
}
fetchData();
}, [sessionToken]);
But the only issue is that, i can't pass Shopify admin api data for example, i have been trying to send below query and try to fetch in checkout extension, but somehow below code is not working:
export const action: ActionFunction = async ({ request }) => {
const { cors } = await authenticate.public.checkout(request);
const { session } = await authenticate.admin(request);
if (!session) {
console.error("Admin session is missing!");
return cors(json({ error: "Unauthorized" }, { status: 401 }));
}
try {
const query = `
{
products(first: 2) {
edges {
node {
title
}
}
}
}
`;
const response = await session.graphql(query);
console.log("Raw GraphQL Response:", JSON.stringify(response, null, 2));
if (!response?.data?.products) {
console.error("No products returned from Shopify GraphQL!");
throw new Error("Failed to fetch products.");
}
const products = response.data.products.edges.map((edge: any) => edge.node.title);
console.log("Product Titles:", products);
return cors(json({ message: "POST success", products }));
} catch (error) {
console.error("GraphQL Error:", error);
return cors(json({ error: error.message }, { status: 500 }));
}
};
Your help is much appreciated.
Hi @Kit_
Appreciate you sharing your challenges here.
I have the same issue... When to authenticate the request in the backend with:
const { session } = await authenticate.admin(request);
I get 500 error and I see below on my terminal.
Authenticating admin request | { shop: null }
If you find a solution, I’d really appreciate it if you could share it with me:-)