1 const hostname = process.env.REPLIT_CONNECTORS_HOSTNAME;
2 const token = process.env.REPL_IDENTITY
3 ? `repl ${process.env.REPL_IDENTITY}`
4 : process.env.WEB_REPL_RENEWAL
5 ? `depl ${process.env.WEB_REPL_RENEWAL}`
6 : null;
7
8 const [bodyJson, pathArg] = process.argv.slice(2);
9 const path = pathArg || "/admin/api/2026-04/graphql.json";
10
11 if (!hostname || !token) {
12 console.log("Missing Replit connector environment. Run inside the Repl shell.");
13 process.exit(1);
14 }
15
16 if (!bodyJson) {
17 console.log("Usage: node shopify-admin-api.mjs '{\"query\":\"query { shop { name } }\"}' [path]");
18 process.exit(0);
19 }
20
21 const protocol = hostname.startsWith("localhost") ? "http" : "https";
22
23 const resp = await fetch(`${protocol}://${hostname}/api/v2/proxy${path}`, {
24 method: "POST",
25 headers: {
26 "Content-Type": "application/json",
27 "X-Replit-Token": token,
28 "Connector-Name": "shopify-store",
29 },
30 body: bodyJson,
31 signal: AbortSignal.timeout(30_000),
32 });
33
34 const text = await resp.text();
35 let parsed = null;
36 try {
37 parsed = JSON.parse(text);
38 console.log(JSON.stringify(parsed, null, 2));
39 } catch {
40 console.log(text);
41 }
42
43 // Shopify Admin GraphQL returns HTTP 200 even when the response body has
44 // top-level `errors`, and mutation validation failures show up in nested
45 // `userErrors`. Fail loudly so chained setup scripts (`node ... && ...`)
46 // stop on those instead of treating a failed Admin call as success.
47 function hasUserErrors(node) {
48 if (!node || typeof node !== "object") return false;
49 if (Array.isArray(node)) return node.some(hasUserErrors);
50 if (Array.isArray(node.userErrors) && node.userErrors.length > 0) return true;
51 for (const value of Object.values(node)) {
52 if (hasUserErrors(value)) return true;
53 }
54 return false;
55 }
56
57 const hasTopLevelErrors =
58 parsed && Array.isArray(parsed.errors) && parsed.errors.length > 0;
59 const hasMutationUserErrors = parsed && hasUserErrors(parsed.data);
60
61 if (!resp.ok || hasTopLevelErrors || hasMutationUserErrors) {
62 process.exitCode = 1;
63 }