- Published on
Setting Up an App Proxy in Shopify Fetching Data in Frontend Extension with Remix JS
- Authors
- Name
- Entaice Braintrust
Setting Up an App Proxy in Shopify: Fetching Data in Frontend Extension with Remix JS
You know those moments when you think you’ve got everything under control, and then reality decides to throw a wrench in your plans? Kind of like when you think your cat finally decided to love you but then it bites. Well, I recently had one of those moments while working with Shopify and Remix JS, which thankfully led to some rewarding discoveries I’m now excited to share. Let’s journey together and transform those pesky technical challenges into opportunities for learning and innovation.
Understanding App Proxies: The Unexpected Tale of a Simplified Data Fetching
Imagine, if you will, an epic quest—not unlike searching for the Holy Grail—where your goal is to fetch data from a gilded backend realm to present it gloriously on the frontend. That’s the journey our good friend, the App Proxy, embarks on within Shopify when dealing with external requests. To align our operation with this valiant proxy, let's dig deep into how we can connect our Remix JS application efficiently.
First Steps: The Basics of Setting Up Your App Proxy
Before we dive headfirst into the code, let’s take a warm-up lap by setting up the App Proxy in Shopify. Think of this as making sure you've packed a map before hitting the trail. After you've made your coffee and sat comfortably, head to the Shopify Partner Dashboard.
Navigate to Your App Setup: Go to your Apps, then identify your fearless app from the list and click on it.
App Proxy Landing: In the App setup, find "App Proxy Settings." This is where the magic begins, or as close as you can get without a wand.
Configuring the Proxy: Enter the required “Subpath prefix” and the “Subpath”. It’s like naming your sled dogs for the Iditarod—super important. Here, you might enter something edifying like
/apps/zip-code
.Proxy Subpath Endpoint: `/apps/zip-code` Subpath Prefix: `/calculate-shipping`,
Target URL in Your App: This should match the route you have handling the proxy endpoint in your server:
Target URL: `https://yourappserver.com/app/calculateshipping`
Ensure you save changes—Shopify likes to forget things if you don’t.
Bridging the Backend and Frontend: A Sticky Situation Resolved
Now, you’ve got your proxy soldiers in place. But how does this alliance benefit your code? Let’s paint this picture with some delightful Remix code refinements.
Revisiting the Loader Function
The loader function we discussed in a Shakespearean manner before needs a slight nudge. Let’s ensure it's robust enough to handle the demands of a user’s zip code query.
import { json, LoaderFunction } from "@remix-run/node";
import { authenticate } from "../shopify.server"; // Make sure the path is correct
export const loader: LoaderFunction = async ({ request }) => {
const { admin } = await authenticate.admin(request);
const urlParams = new URL(request.url).searchParams;
const zipCode = urlParams.get("zip");
if (!zipCode) {
return json({ error: "Zip code is required" }, { status: 400 });
}
try {
// Fetch from Shopify to grab metafields
const response = await admin.graphql(`
query {
product(id: "gid://shopify/Product/8304181903529") {
metafields(namespace: "custom_app", first: 100) {
edges {
node {
key
value
}
}
}
}
}
`);
const parsedResponse = await response.json();
const metafields = parsedResponse.data?.product?.metafields?.edges || [];
for (const { node } of metafields) {
const data = JSON.parse(node.value);
if (data.some(entry => entry.range.includes(zipCode))) {
const matchingEntry = data.find(entry => entry.range.includes(zipCode));
return json({ shipping_cost: matchingEntry.price }, { status: 200 });
}
}
return json({ error: "No shipping cost found for this zip code" }, { status: 404 });
} catch (error) {
console.error("Error fetching data:", error);
return json({ error: "Internal server error" }, { status: 500 });
}
};
This code is the sturdy bridge that gets our data across. It’s clear, concise, and lets us know exactly who’s holding things up if it crashes and burns (which it won’t).
Frontend Invocation: Let’s Make It Dance
Alright, now for the front-end companion. Imagine a zesty radio talk show host calling on their party guests—this is how our Javascript beckons the backend to reveal its treasures.
<div>
<label class="zipcode-label" for="zip-code-input">Estimated Shipping</label>
<div class="input-field-btn">
<input type="text" id="zip-code-input" name="zip-code" placeholder="Enter zipcode or postcode">
<button id="calculate-shipping-btn" type="button">Find</button>
</div>
<p id="shipping-cost-output"></p>
</div>
<script>
document.getElementById('calculate-shipping-btn').addEventListener('click', async function() {
const zipCode = document.getElementById('zip-code-input').value.trim();
if (!zipCode) {
alert('Please enter a valid zip code.');
return;
}
document.getElementById('shipping-cost-output').innerText = 'Calculating...';
try {
const response = await fetch(`/apps/zip-code/calculate-shipping?zip=${zipCode}`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
if (!response.ok) {
throw new Error('Network response was not ok.');
}
const data = await response.json();
if (data.shipping_cost) {
document.getElementById('shipping-cost-output').innerText = `Shipping Cost: $${data.shipping_cost}`;
} else if (data.error) {
document.getElementById('shipping-cost-output').innerText = data.error;
}
} catch (error) {
console.error('Error fetching shipping cost:', error);
document.getElementById('shipping-cost-output').innerText = 'Error calculating shipping cost.';
}
});
</script>
Embracing the simplicity and connectivity provided by App Proxy works wonders. It’s like having a smooth jazz number sync so beautifully with a delicate soufflé rising in the oven — everything just clicks.
A Parting of Wisdom
So here we stand, on the other side of our little odyssey, having battled through the land of configurations and codes. The gremlin that was once a daunting code bug now seems more like a cute, albeit slightly mischievous, friend.
And in this shared experience—side by side in the grand, often maddening world of web development—let’s remember to savor the small victories. Who knows, maybe next time we’ll be fighting different dragons, but armed with more confidence and a cup of excellent coffee.