API 404 Error in Custom Shopify Reviews App

API 404 Error in Custom Shopify Reviews App

raza97
Shopify Partner
7 0 1

Issue Description:

I’m building a simple Shopify review app with two APIs:

  1. /app/routes/api/review.jsx → Works perfectly (displays all reviews to  merchant in a table).

  2. /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.


Current Setup:

  • Framework: Shopify Remix

  • Dev Command: shopify app dev --use-localhost

  • Theme App Extension: Enabled

  • Store: content-writer-app.myshopify.com (Password: iabayr)

Relevant Files:

  1. reviews-block.liquid (Extensions Block)

  2. reviews-widget.js (Extensions Assets)

  3. api.reviews.jsx (Failing API)


Troubleshooting Steps Taken:

  • Verified file paths and naming conventions.

  • Tested API endpoints manually ( browser).
  • Confirmed the block is properly registered in the theme.


Request for Help:

Could someone review the code snippets below or suggest common pitfalls for:

  1. Remix API routing issues (why one API works but the other doesn’t)?

  2. Theme App Extension compatibility (e.g., CORS, authentication)?

  3. 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.";
    }
  });
  
Replies 0 (0)