Skip to content

Testing Up to Phase 2 (Product Sync & Ingestion)

This guide explains how to test the app through Phase 0, 1, and 2: OAuth, session storage, shop persistence, product sync, and ingestion (chunks + embeddings). Use it after you have implemented Phase 0–2.


Before testing, ensure:

ItemAction
DatabasePostgres with pgvector is running (e.g. Dokploy). Migrations applied: cd appifire-ai-chat && npx prisma migrate deploy
.envIn appifire-ai-chat/, copy .env.sample to .env and set: DATABASE_URL, DIRECT_URL, SHOPIFY_API_KEY, SHOPIFY_API_SECRET, SCOPES, SHOPIFY_APP_URL, and OPENROUTER_API_KEY (required for embeddings in Phase 2).
ShopifyPartner account + at least one development store with some products (even 1–2 is enough).
Shopify CLILogged in: shopify login (same email as Partner account).

From the app folder:

Terminal window
cd appifire-ai-chat
npm install
npx prisma generate
npm run dev

This runs shopify app dev: it starts the Remix server and opens a tunnel. The CLI will print a URL like https://xxx.trycloudflare.com or similar — that is your app URL for this session.

  • If the Partner app is still using a fixed URL (e.g. https://ai-chat.appifire.com), install/re-auth will hit that URL. For local testing you need the app to be reachable at the tunnel URL. Either:
    • In Partner Dashboard → your app → App setup → set App URL to the tunnel URL shown by shopify app dev for this run, or
    • Use the CLI’s “Preview URL” to install the app (the CLI often gives you a link that uses the tunnel).
  • When you install or re-authorize the app on a dev store, the browser will go through OAuth and then load the app. That triggers afterAuth and thus the full product sync in the background.

Sync runs automatically in two cases:

  1. Install — When a merchant installs the app (OAuth completes), afterAuth runs and calls syncAllProducts({ shopId, admin }).
  2. Re-auth — If the app was already installed and you change scopes or re-open the app and Shopify re-runs OAuth, afterAuth runs again and sync runs again.

How to trigger it:

  • First time: In the Partner Dashboard, open your app → Test your app (or use the install link for your dev store). Complete install; the tunnel must be the app URL as in step 2.
  • Re-trigger: Uninstall the app from the dev store, then install again. Or use “Test your app” again if the CLI offers a new session.

Sync is fire-and-forget: the OAuth callback returns immediately; sync and ingestion run in the background. So you verify via logs and database, not the browser.


In the terminal where npm run dev is running, look for:

  • [afterAuth] Shop upserted: your-store.myshopify.com (id: ...)
    → Shop record created/updated.
  • [product-sync] Starting full sync for shop ..., job ...
    → Sync started.
  • [product-sync] Found N products to sync
    → Products fetched from Shopify.
  • [product-sync] Completed sync for shop ...: N/N products
    → Sync finished.
  • Any [product-sync] Failed to sync product ... or [afterAuth] product sync failed:
    → Investigate (e.g. missing OPENROUTER_API_KEY, DB or API errors).

If you see “Found 0 products”, add at least one product in the dev store (Products → Add product) and trigger sync again (re-install or re-auth).


Use either Prisma Studio or SQL.

Terminal window
cd appifire-ai-chat
npx prisma studio

Then:

TableWhat to check
ShopOne row for your dev store; shopDomain, accessToken (non-empty), status: active.
ProductRows per Shopify product; shopId = that shop’s UUID; shopifyProductId, title, etc.
ProductVariantRows per variant; productId matches Product.id.
KnowledgeSourceOne row per shop with type: 'product' (or as in your code).
KnowledgeDocumentOne row per product; sourceId = that source; externalRefId = Shopify product ID.
KnowledgeChunkChunks for each document (overview + variant chunks).
EmbeddingOne row per chunk; embedding is a vector (in Studio you may see “[object]” or similar).
IngestionJobLatest job for the shop: status: 'completed', totalDocuments / processedDocuments = product count.

Option B: SQL (e.g. psql or Dokploy SQL console)

Section titled “Option B: SQL (e.g. psql or Dokploy SQL console)”
-- Shops
SELECT id, "shopDomain", "plan", status FROM shops;
-- Product count per shop
SELECT s."shopDomain", COUNT(p.id) AS products
FROM shops s
LEFT JOIN products p ON p."shopId" = s.id
GROUP BY s.id, s."shopDomain";
-- Chunks and embeddings per shop
SELECT s."shopDomain",
COUNT(DISTINCT kd.id) AS documents,
COUNT(kc.id) AS chunks,
COUNT(e.id) AS embeddings
FROM shops s
JOIN knowledge_sources ks ON ks."shopId" = s.id
JOIN knowledge_documents kd ON kd."sourceId" = ks.id
LEFT JOIN knowledge_chunks kc ON kc."documentId" = kd.id
LEFT JOIN embeddings e ON e."chunkId" = kc.id
GROUP BY s.id, s."shopDomain";

If sync and ingestion succeeded, you should see products, documents, chunks, and embeddings for your test shop.


Phase 2 adds webhooks for product create/update/delete. To test:

  1. Create: In Shopify admin → Products → Add product. Save.
    • In DB: new row in Product, ProductVariant, and corresponding KnowledgeDocument / chunks / embeddings (or updated if you use same externalRefId).
  2. Update: Edit an existing product (title or description) and save.
    • In DB: Product and related chunks/embeddings updated (ingestion is idempotent via hash).
  3. Delete: Delete a product in Shopify admin.
    • In DB: that product (and its variants) removed; cascades should remove related knowledge documents/chunks/embeddings if your schema uses onDelete: Cascade.

Webhooks are sent to your app’s public URL. So:

  • Local: Use the same tunnel URL as in step 2 and ensure Shopify has that URL as the app URL (or that webhook subscription uses the tunnel). Shopify sends webhooks to the app URL (e.g. https://your-tunnel.trycloudflare.com/webhooks/products/create etc.).
  • Deployed: Use https://ai-chat.appifire.com (or your real app URL); webhooks go there.

Check logs for webhook requests; any 4xx/5xx will show in the terminal. You can also use Partner Dashboard → your app → Webhooks to see delivery status.


SymptomLikely causeFix
No [product-sync] logsSync not triggered or app URL not tunnelRe-install app using tunnel URL; confirm afterAuth runs (you should see “Shop upserted”).
“Found 0 products”Store has no products or API scopeAdd a product in dev store; ensure read_products (and needed scopes) in shopify.app.toml and .env SCOPES.
Embeddings errors / 401OpenRouter key missing or invalidSet OPENROUTER_API_KEY in .env; add credits in OpenRouter dashboard.
DB errors (e.g. unique constraint)Schema/migration mismatchRun npx prisma migrate deploy and npx prisma generate; ensure DB has pgvector and vector(1536) for embeddings.
Webhooks 404Wrong app URL or routesConfirm app URL in Partner Dashboard matches where the app runs (tunnel or production); ensure routes exist: webhooks.products.create, webhooks.products.update, webhooks.products.delete.

  • .env has OPENROUTER_API_KEY, DB URLs, and Shopify vars.
  • npx prisma migrate deploy and npx prisma generate run without errors.
  • npm run dev starts; tunnel URL is used as app URL (or production URL) for install.
  • Install (or re-install) app on a dev store that has at least one product.
  • Logs show “Shop upserted” and “Completed sync … N/N products”.
  • In DB: shops, products, product_variants, knowledge_*, embeddings, ingestion_jobs look correct.
  • (Optional) Create/update/delete a product and confirm DB and webhook logs.

Once this passes, you have verified the stack through Phase 2 and can proceed to Phase 3 (RAG & Chat API).