App reviews, troubleshooting, and recommendations
I’m building a simple Shopify review app with two APIs:
/app/routes/api/review.jsx → Works perfectly (displays all reviews to merchant in a table).
/app/routes/api/api.reviews.jsx → Returns a 404 error when fetching reviews for a specific product on the product page.
The second API is supposed to fetch reviews from the database for a single product, but it fails with ERROR LOADING REVIEWS on the frontend.
Framework: Shopify Remix
Dev Command: shopify app dev --use-localhost
Theme App Extension: Enabled
Store: content-writer-app.myshopify.com (Password: iabayr)
reviews-block.liquid (Extensions Block)
reviews-widget.js (Extensions Assets)
api.reviews.jsx (Failing API)
Verified file paths and naming conventions.
Confirmed the block is properly registered in the theme.
Could someone review the code snippets below or suggest common pitfalls for:
Remix API routing issues (why one API works but the other doesn’t)?
Theme App Extension compatibility (e.g., CORS, authentication)?
Debugging 404s in Shopify’s local development environment?
api.reviews.jsx
import prisma from "../../db.server"; export const loader = async ({ request }) => { const url = new URL(request.url); const productId = url.searchParams.get("productId"); if (!productId) { return new Response(JSON.stringify({ error: "Missing productId" }), { status: 400, headers: { "Content-Type": "application/json" }, }); } const reviews = await prisma.review.findMany({ where: { productId, status: "APPROVED", }, orderBy: { createdAt: "desc", }, }); return new Response(JSON.stringify({ reviews }), { headers: { "Content-Type": "application/json" }, }); };
reviews-block.liquid
{% schema %} { "name": "Reviews Widget", "target": "section", "settings": [] } {% endschema %} <div id="ugc-reviews-widget" data-product-id="{{ product.id }}" style="padding: 20px;"> <!-- Loading/Empty/Error Message --> <div id="ugc-reviews-message" style="text-align: center; color: #888;">Loading reviews...</div> <!-- Reviews list --> <div id="ugc-reviews-container" style="display: none; margin-top: 20px;"></div> </div> <script src="{{ 'reviews-widget.js' | asset_url }}" defer></script>
reviews-widget.js
document.addEventListener("DOMContentLoaded", async () => { const widget = document.getElementById("ugc-reviews-widget"); const productId = widget.getAttribute("data-product-id"); try { const response = await fetch(`/apps/all-in-one-writer-1/api/reviews?productId=${productId}`); const data = await response.json(); const message = document.getElementById("ugc-reviews-message"); const container = document.getElementById("ugc-reviews-container"); if (!data || !data.reviews) { message.innerText = "Failed to load reviews."; return; } if (data.reviews.length === 0) { message.innerText = "No reviews yet. Be the first to review!"; return; } // Reviews loaded message.style.display = "none"; container.style.display = "block"; data.reviews.forEach((review) => { const card = document.createElement("div"); card.style.padding = "16px"; card.style.marginBottom = "16px"; card.style.border = "1px solid #eee"; card.style.borderRadius = "8px"; card.style.boxShadow = "0 2px 8px rgba(0,0,0,0.05)"; card.style.backgroundColor = "#fff"; card.innerHTML = ` <div style="font-weight: bold; font-size: 16px;">${review.customerName || "Anonymous"}</div> <div style="margin: 4px 0; color: #ffc107; font-size: 18px;"> ${'⭐'.repeat(review.rating)} </div> ${review.title ? `<div style="font-weight: 500; margin: 8px 0;">${review.title}</div>` : ''} <div style="color: #555;">${review.body}</div> `; container.appendChild(card); }); } catch (error) { const message = document.getElementById("ugc-reviews-message"); message.innerText = "Error loading reviews."; } });
Learn how to build powerful custom workflows in Shopify Flow with expert guidance from ...
By Jacqui May 7, 2025Did You Know? May is named after Maia, the Roman goddess of growth and flourishing! ...
By JasonH May 2, 2025Discover opportunities to improve SEO with new guidance available from Shopify’s growth...
By Jacqui May 1, 2025