{"id":17044,"plugin_id":"plugins_6a701c7b1f9481919cf7c7448ddc1bd4","kind":"skill","collection_source":null,"comparison_source":null,"observed_at":"2026-09-30T23:13:56.349Z","digest":"c4876429d0c0df59b3b0b433c3d2b97c49512c0b751d99aae4918ec5359a1f35","against":null,"payload":{"description":"Use when scaffolding a new Shopify app, running Shopify CLI commands (shopify app dev/deploy/generate), configuring shopify.app.toml, generating app extensions (admin/checkout/theme/function), debugging tunnels or auth issues, or working with the official Remix/Node/PHP/Ruby app templates. Trigger on 'shopify app', 'shopify cli', 'shopify init', 'shopify dev', 'shopify deploy', 'generate extension', 'shopify.app.toml', 'remix template', 'tunnel', 'ngrok', 'cloudflare tunnel', 'ME APP_URL', 'SHOPIFY_API_KEY', or anything involving the Shopify CLI workflow.","included_files":[],"name":"shopify-cli","skill_md_contents":"---\nname: shopify-cli\ndescription: \"Use when scaffolding a new Shopify app, running Shopify CLI commands (shopify app dev/deploy/generate), configuring shopify.app.toml, generating app extensions (admin/checkout/theme/function), debugging tunnels or auth issues, or working with the official Remix/Node/PHP/Ruby app templates. Trigger on 'shopify app', 'shopify cli', 'shopify init', 'shopify dev', 'shopify deploy', 'generate extension', 'shopify.app.toml', 'remix template', 'tunnel', 'ngrok', 'cloudflare tunnel', 'ME APP_URL', 'SHOPIFY_API_KEY', or anything involving the Shopify CLI workflow.\"\n---\n\n# Shopify CLI Skill Reference\n\n## When to Use This Skill\n\nUse this skill for any task involving:\n- Creating a new Shopify app with `shopify app init`\n- Running development server with `shopify app dev`\n- Deploying apps with `shopify app deploy` or `shopify app release`\n- Generating extensions (admin actions, checkout UI, theme extensions, functions)\n- Configuring `shopify.app.toml` and understanding all available settings\n- Working with Remix + React Router v7 app template\n- Setting up local tunnels (Cloudflare Tunnel or ngrok)\n- Debugging authentication, webhooks, or environment issues\n- Managing theme development with `shopify theme` commands\n- Generating GraphQL types with `@shopify/api-codegen-preset`\n- Understanding app structure, directory layout, and lifecycle\n- Error troubleshooting and common failure patterns\n\n## Installation and Setup\n\n### Install Shopify CLI\n\n```bash\nnpm install -g @shopify/cli@latest\n```\n\n**Node.js Requirements:**\n- `>=20.19 <22` OR `>=22.12`\n- Check version: `node --version`\n\n### Verify Installation\n\n```bash\nshopify version\nshopify app --help\n```\n\n## Command Reference\n\n| Command | Flags | Purpose |\n|---------|-------|---------|\n| `shopify app init` | `--template remix` | Initialize new Shopify app with Remix template |\n| `shopify app dev` | `--reset`, `--build`, `--no-update` | Start local dev server with hot reload |\n| `shopify app deploy` | `--force`, `--no-release` | Deploy app to Shopify Partner dashboard |\n| `shopify app release` | `--version X.Y.Z`, `--force` | Release deployed version to live |\n| `shopify app generate` | `extension`, `webhook` | Generate boilerplate for extensions/webhooks |\n| `shopify generate extension` | `--type admin_action`, `--api-version 2026-07` | Create app extension with a currently supported API version |\n| `shopify app push` | `--force`, `--no-release` | Push config updates without full deploy |\n| `shopify config show` | | Display loaded config from shopify.app.toml |\n| `shopify env pull` | | Fetch environment variables from Partner dashboard |\n| `shopify app open` | | Open app dashboard in browser |\n| `shopify theme dev` | `--store example.myshopify.com` | Start theme development server |\n| `shopify theme pull` | `--theme-id 123456789` | Download theme files from store |\n| `shopify theme push` | `--force`, `--no-delete` | Upload theme files to store |\n| `shopify auth logout` | | Clear stored authentication |\n| `shopify auth login` | `--shop example.myshopify.com` | Authenticate with specific store |\n\n## shopify app init Workflow (6-Step Process)\n\n### Step 1: Launch Init Command\n```bash\nshopify app init --template remix\n```\n\n### Step 2: Provide Org and App Name\n```\n? App name\n> my-shopify-app\n\n? Org\n> Select from: [list of partner orgs]\n```\n\n### Step 3: Select Package Manager\n```\n? Package manager\n> npm / yarn / pnpm\n```\n\n### Step 4: Create Local Tunnel\n```\n? Local tunnel authentication\n> Cloudflare / ngrok / skip\n```\n\n### Step 5: Install Dependencies\n```bash\ncd my-shopify-app\nnpm install\n```\n\n### Step 6: Start Dev Server\n```bash\nnpm run dev\n```\n\nOutput:\n```\n✓ Tunnel started at https://RANDOMHASH.lhr.life\n✓ App URL: https://RANDOMHASH.lhr.life/api/auth\n✓ Admin API access scopes configured\n✓ Listening on port 3000\n```\n\n## Remix Template Deep Dive\n\n### Directory Structure\n\n```\nmy-shopify-app/\n├── shopify.app.toml              # App configuration (CRITICAL)\n├── remix.config.js               # Remix build config\n├── package.json                  # Dependencies\n├── .env.example                  # Environment template\n├── prisma/\n│   ├── schema.prisma            # Database schema\n│   └── migrations/              # Database migrations\n├── app/\n│   ├── shopify.server.ts        # Shopify API setup (CRITICAL)\n│   ├── db.server.ts             # Database connection\n│   ├── root.tsx                 # Root layout\n│   ├── routes/\n│   │   ├── _index.tsx           # Dashboard\n│   │   ├── api/\n│   │   │   ├── auth/\n│   │   │   │   ├── callback.ts  # OAuth callback\n│   │   │   │   └── login.ts     # OAuth initiate\n│   │   │   └── webhooks/\n│   │   │       └── orders.ts    # Webhook handler\n│   │   ├── app/\n│   │   │   └── dashboard/\n│   │   │       └── _index.tsx   # App dashboard\n│   │   └── admin-actions/\n│   │       └── bulk-operation.tsx\n│   ├── components/\n│   │   └── Navigation.tsx\n│   └── styles/\n│       └── app.css\n└── public/\n    └── images/\n```\n\n### shopify.server.ts (Authentication Setup)\n\n```typescript\nimport { shopifyApp } from \"@shopify/shopify-app-remix/server\";\nimport { PrismaSessionStorage } from \"@shopify/shopify-app-session-storage-prisma\";\nimport { restResources } from \"@shopify/shopify-api/rest/admin/2026-07\";\nimport { prisma } from \"./db.server\";\n\nconst shopify = shopifyApp({\n  apiKey: process.env.SHOPIFY_API_KEY!,\n  apiSecret: process.env.SHOPIFY_API_SECRET!,\n  scopes: (process.env.SCOPES || \"\").split(\",\"),\n  host: process.env.HOST!,\n  isEmbeddedApp: false,\n  sessionStorage: new PrismaSessionStorage(prisma),\n  distribution: {\n    saleChannel: \"2152896513\",\n    surface: \"admin_home_surfaces\",\n  },\n  restResources,\n  webhooks: {\n    APP_INSTALLED: {\n      deliveryMethod: \"Http\",\n      callbackUrl: \"/api/webhooks/app-installed\",\n    },\n    APP_UNINSTALLED: {\n      deliveryMethod: \"Http\",\n      callbackUrl: \"/api/webhooks/app-uninstalled\",\n    },\n  },\n});\n\nexport default shopify;\n```\n\n### db.server.ts (Database Connection)\n\n```typescript\nimport { PrismaClient } from \"@prisma/client\";\n\nlet prisma: PrismaClient;\n\ndeclare global {\n  var __db: PrismaClient | undefined;\n}\n\nif (process.env.NODE_ENV === \"production\") {\n  prisma = new PrismaClient();\n} else {\n  if (!global.__db) {\n    global.__db = new PrismaClient();\n  }\n  prisma = global.__db;\n}\n\nexport { prisma };\n```\n\n### prisma/schema.prisma (Data Models)\n\n```prisma\ndatasource db {\n  provider = \"sqlite\"\n  url      = env(\"DATABASE_URL\")\n}\n\ngenerator client {\n  provider = \"prisma-client-js\"\n}\n\nmodel Session {\n  id    String @id\n  shop  String\n  state String\n  isOnline Boolean @default(false)\n  scope String?\n  expires Int?\n  accessToken String\n  refreshToken String?\n  user Json?\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\nmodel Product {\n  id        String  @id\n  shopifyId String  @unique\n  title     String\n  handle    String\n  status    String\n  vendor    String?\n  productType String?\n  createdAt DateTime @default(now())\n  updatedAt DateTime @updatedAt\n}\n\nmodel Order {\n  id        String  @id\n  shopifyId String  @unique\n  email     String\n  totalPrice String\n  createdAt DateTime @default(now())\n}\n```\n\n### Environment Variables (.env)\n\n```env\nSHOPIFY_API_KEY=YOUR_API_KEY\nSHOPIFY_API_SECRET=YOUR_API_SECRET\nSHOPIFY_APP_ID=YOUR_APP_ID\nSCOPES=write_products,read_orders,write_inventory\nHOST=https://RANDOMHASH.lhr.life\nDATABASE_URL=file:./dev.db\nNODE_ENV=development\n```\n\n### package.json (Key Dependencies)\n\n```json\n{\n  \"dependencies\": {\n    \"@shopify/shopify-app-remix\": \"^4.1.0\",\n    \"@shopify/polaris\": \"^12.0.0\",\n    \"remix\": \"^2.0.0\",\n    \"react-router\": \"^7.0.0\",\n    \"prisma\": \"^5.0.0\",\n    \"@prisma/client\": \"^5.0.0\"\n  },\n  \"devDependencies\": {\n    \"@shopify/api-codegen-preset\": \"^1.0.0\",\n    \"typescript\": \"^5.0.0\"\n  },\n  \"scripts\": {\n    \"dev\": \"remix dev --manual\",\n    \"build\": \"remix build\",\n    \"start\": \"remix-serve build\",\n    \"type-check\": \"tsc --noEmit\",\n    \"graphql-codegen\": \"graphql-codegen --config codegen.ts\"\n  }\n}\n```\n\n## shopify.app.toml Schema Reference\n\n### Complete Example with All Sections\n\n```toml\nscopes = \"write_products,read_orders,write_inventory,read_fulfillments\"\ntitle = \"My Shopify App\"\ndescription = \"App that manages products and inventory\"\n\n[build]\nautomatically_update_urls_on_dev = true\ndev_store_url = \"dev-store.myshopify.com\"\n\n[auth]\nredirect_urls = [\n  \"https://example.com/api/auth/callback\"\n]\n\n[webhooks]\napi_version = \"2026-07\"\n\n[[webhooks.subscriptions]]\ntopics = [\"products/create\", \"products/update\"]\nuri = \"api/webhooks/products\"\nfilter_query = \"status:active\"\ninclude_fields = [\"id\", \"title\", \"handle\", \"status\"]\n\n[[webhooks.subscriptions]]\ntopics = [\"orders/create\"]\nuri = \"api/webhooks/orders\"\n\n[pos]\nembedded = false\n\n[admin]\nembedded = true\n\n[[app_extensions]]\ntype = \"admin_action\"\nhandle = \"bulk-edit-products\"\nlabel = \"Bulk Edit Products\"\n\n[[app_extensions]]\ntype = \"checkout_ui\"\nhandle = \"post-purchase-upsell\"\nconfiguration = \"checkout.json\"\n\n[[app_extensions]]\ntype = \"theme_app_extension\"\nhandle = \"theme-blocks\"\n\n[[app_extensions]]\ntype = \"product_discount\"\nhandle = \"volume-discount\"\n\n[[app_extensions]]\ntype = \"shipping_discount\"\nhandle = \"free-shipping\"\n\n[settings]\nfields = [\n  { key = \"sync_enabled\", type = \"boolean\", default = true },\n  { key = \"max_products\", type = \"number\", default = 100 },\n  { key = \"webhook_delay\", type = \"number\", default = 5 }\n]\n```\n\n## Extension Types Reference\n\n### admin_action\nExtends Admin UI with custom buttons/actions in product, order, or customer pages.\n\n```typescript\n// extensions/admin-action/src/index.tsx\nimport { extend, Button, Section } from \"@shopify/ui-extensions/admin\";\n\nexport default extend(\"admin.product-details.action.render\", (root, api) => {\n  root.appendChild(\n    root.createElement(Button, {\n      onPress: () => {\n        api.toast.show(\"Action triggered!\");\n      },\n    }, \"Custom Action\")\n  );\n});\n```\n\n### checkout_ui\nCustomize checkout flow. Limited to specific UI points.\n\n```typescript\n// extensions/checkout-ui/src/index.tsx\nimport { extend, TextField } from \"@shopify/ui-extensions/checkout\";\n\nexport default extend(\"purchase.checkout.contact-email.render-before\", (root) => {\n  root.appendChild(\n    root.createElement(TextField, {\n      label: \"Referral Code\",\n      onChange: (value) => console.log(value),\n    })\n  );\n});\n```\n\n### theme_app_extension\nAdd liquid blocks/sections to theme editor.\n\n```json\n// extensions/theme/blocks/custom-section.json\n{\n  \"name\": \"Custom Section\",\n  \"target\": \"section\",\n  \"settings\": [\n    {\n      \"type\": \"text\",\n      \"id\": \"title\",\n      \"label\": \"Section Title\"\n    }\n  ]\n}\n```\n\n### product_discount / shipping_discount / payment_customization\nPricing functions—JavaScript executed server-side during checkout.\n\n```javascript\n// extensions/product-discount/src/index.js\nexport function run(input) {\n  return input.lines\n    .filter(line => line.quantity > 5)\n    .map(line => ({\n      cartLineId: line.id,\n      percentageDecrease: {\n        value: 10.0\n      }\n    }));\n}\n```\n\n### post_purchase_ui\nShow custom page after purchase confirmation.\n\n```typescript\n// extensions/post-purchase/src/index.tsx\nimport { extend, Heading, Button } from \"@shopify/ui-extensions/post_purchase\";\n\nexport default extend(\"purchase.post-purchase.block.render\", (root, api) => {\n  root.appendChild(\n    root.createElement(Heading, {}, \"Thank you for your purchase!\")\n  );\n});\n```\n\n## Local Development Workflow\n\n### Start Dev Server\n\n```bash\nnpm run dev\n```\n\nExpected output:\n```\n✓ Tunnel created at https://RANDOMHASH.lhr.life\n✓ App URL: https://RANDOMHASH.lhr.life/api/auth\n✓ Admin API credentials loaded\n✓ Database: SQLite (dev.db)\n✓ Webhooks: 3 subscriptions configured\n✓ HMR active on port 3000\n✓ Listening on all interfaces\n```\n\n### Hot Module Replacement (HMR)\n\nHMR is enabled by default. Changes to:\n- `.tsx` files in `/app/routes` → auto-reload\n- `.ts` files in `/app` → server restart\n- `shopify.app.toml` → restart required\n\nDo NOT manually restart; HMR handles reloads.\n\n### Environment Injection During Dev\n\nThe tunnel URL is automatically injected as:\n- `HOST=https://RANDOMHASH.lhr.life`\n- `SHOPIFY_APP_ID` from shopify.app.toml\n- Scopes from shopify.app.toml\n\n### Webhook Testing in Local Dev\n\nConfigure webhooks in Partner dashboard to point to tunnel URL:\n```\nhttps://RANDOMHASH.lhr.life/api/webhooks/products\n```\n\nTest webhook delivery:\n```bash\ncurl -X POST https://RANDOMHASH.lhr.life/api/webhooks/products \\\n  -H \"Content-Type: application/json\" \\\n  -H \"X-Shopify-Hmac-SHA256: SIGNATURE\" \\\n  -d '{\n    \"id\": \"1234567890\",\n    \"title\": \"Test Product\",\n    \"handle\": \"test-product\"\n  }'\n```\n\n### 5 Common Dev Failures and Fixes\n\n**Failure 1: Tunnel Connection Lost**\n```\nError: Tunnel disconnected\n```\nFix: Restart dev server (Ctrl+C, then npm run dev)\n\n**Failure 2: PORT 3000 Already in Use**\n```\nError: EADDRINUSE :::3000\n```\nFix: lsof -i :3000 | kill -9 <PID> or use PORT=3001 npm run dev\n\n**Failure 3: Database Not Found**\n```\nPrismaClientInitializationError: Can't reach database server\n```\nFix: Run migrations: npx prisma migrate dev\n\n**Failure 4: Invalid Scopes in shopify.app.toml**\n```\nError: Invalid scope 'read_prodcuts'\n```\nFix: Check spelling in scopes = \"...\" line (e.g., read_products)\n\n**Failure 5: Webhook Signature Mismatch**\n```\nError: HMAC verification failed\n```\nFix: Ensure webhook secret in handler matches SHOPIFY_API_SECRET in .env\n\n## Deployment Workflow\n\n### Pre-Deployment Checklist\n\n- Update version in shopify.app.toml: version = \"1.0.1\"\n- Run tests: npm test\n- Build: npm run build\n- Check for errors: npm run type-check\n- Commit changes: git commit -am \"Release v1.0.1\"\n\n### Deploy Command\n\n```bash\nshopify app deploy\n```\n\nOutput:\n```\n✓ Validating shopify.app.toml\n✓ Building production bundle\n✓ Uploading to Partner dashboard\n✓ Deployment ID: dpl_XXXXX\n✓ View dashboard: https://partners.shopify.com/dashboard/apps\n```\n\n### Release (Make Live)\n\n```bash\nshopify app release --version 1.0.1\n```\n\nThis makes the deployed version available to merchants. Without release, the app only exists in your Partner dashboard.\n\n### Rollback to Previous Version\n\n```bash\nshopify app deploy --version 1.0.0\nshopify app release --version 1.0.0\n```\n\n### Environment Management\n\nSet environment variables in Partner dashboard:\n1. Go to App Settings → Environment Variables\n2. Add: WEBHOOK_QUEUE_URL, ANALYTICS_API_KEY, etc.\n3. Redeploy to apply\n\nAccess in code:\n```typescript\nconst queueUrl = process.env.WEBHOOK_QUEUE_URL;\n```\n\n## Theme CLI\n\n### Theme Development Server\n\n```bash\nshopify theme dev --store example.myshopify.com\n```\n\nUploads theme files to live store and watches for changes.\n\n### Pull Theme from Store\n\n```bash\nshopify theme pull --theme-id 123456789\n```\n\nDownloads all theme files to /theme directory.\n\n### Push Theme to Store\n\n```bash\nshopify theme push --force --no-delete\n```\n\nUploads local theme files to store. --no-delete prevents deleting files in store.\n\n### Theme File Structure\n\n```\ntheme/\n├── config/\n│   └── settings_schema.json\n├── sections/\n│   ├── header.liquid\n│   └── product.liquid\n├── templates/\n│   ├── index.json\n│   └── product.json\n├── snippets/\n│   └── product-card.liquid\n├── assets/\n│   ├── styles.css\n│   └── main.js\n└── locales/\n    └── en.json\n```\n\n## GraphQL Codegen Setup\n\n### Installation\n\n```bash\nnpm install -D @shopify/api-codegen-preset graphql-codegen\n```\n\n### codegen.ts Configuration\n\n```typescript\nimport type { CodegenConfig } from \"@graphql-codegen/cli\";\n\nconst config: CodegenConfig = {\n  schema: \"https://shopify.dev/admin-api-explorer/latest/graphql.json\",\n  documents: [\"app/**/*.{ts,tsx}\"],\n  generates: {\n    \"generated/graphql.ts\": {\n      preset: \"@shopify/api-codegen-preset\",\n      presetConfig: {\n        apiVersion: \"2026-07\",\n        module: \"graphql-request\",\n      },\n    },\n  },\n};\n\nexport default config;\n```\n\n### Usage Example\n\n```typescript\n// app/routes/products.tsx\nimport { graphql } from \"../generated/graphql\";\nimport { client } from \"../shopify.server\";\n\nconst GetProductsQuery = graphql(`\n  query GetProducts($first: Int!) {\n    products(first: $first) {\n      edges {\n        node {\n          id\n          title\n          handle\n          status\n        }\n      }\n    }\n  }\n`);\n\nexport async function loader({ context }) {\n  const data = await client.query({\n    query: GetProductsQuery,\n    variables: { first: 10 },\n  });\n  return data.products.edges;\n}\n```\n\n### Generate Types\n\n```bash\nnpm run graphql-codegen\n```\n\nGenerates fully-typed GraphQL operations in generated/graphql.ts.\n\n## Common Errors Playbook\n\n### Error 1: \"Cannot find module '@shopify/shopify-app-remix'\"\n\n**Cause:** Missing package in node_modules\n\n**Fix:**\n```bash\nnpm install\nnpm install @shopify/shopify-app-remix@^4.1.0\nnpm run build\n```\n\n### Error 2: \"Invalid SHOPIFY_API_KEY or SHOPIFY_API_SECRET\"\n\n**Cause:** Environment variables not set or incorrect\n\n**Fix:**\n1. Verify in .env: SHOPIFY_API_KEY=xxx and SHOPIFY_API_SECRET=yyy\n2. Check Partner dashboard App Credentials tab\n3. If using Codespace/CI: Add secrets to GitHub Secrets or deployment platform\n\n### Error 3: \"Prisma: Could not find the 'libquery_engine' runtime\"\n\n**Cause:** Prisma binaries not compiled for your platform\n\n**Fix:**\n```bash\nnode <plugin-root>/scripts/reset-shopify-cache.mjs --include prisma/.prisma\nnpm install\nnpx prisma generate\nnpx prisma migrate dev\n```\n\n### Error 4: \"Tunnel URL expires in X minutes\"\n\n**Cause:** Cloudflare free tier tunnel expires after inactivity\n\n**Fix:**\n```bash\nshopify app dev --reset\n# or switch to ngrok:\nshopify app dev --tunnel-provider ngrok\n```\n\n### Error 5: \"No session found for shop example.myshopify.com\"\n\n**Cause:** User not authenticated or session expired\n\n**Fix:**\n```typescript\n// Ensure middleware is loaded:\nimport { sessionMiddleware } from \"@shopify/shopify-app-remix/server\";\n\nexport const loader = async ({ context }) => {\n  const { session } = context;\n  if (!session) {\n    return redirect(\"/api/auth/login\");\n  }\n};\n```\n\n### Error 6: \"Webhook subscription already exists\"\n\n**Cause:** Duplicate webhook registration\n\n**Fix:**\n```bash\nshopify app auth logout\nrm dev.db\nnpm run dev\n# Recreates from scratch\n```\n\n### Error 7: \"Extension type 'admin_action' not supported in API version 2024-10\"\n\n**Cause:** Admin actions require API version 2025-01+\n\n**Fix:** Update shopify.app.toml:\n```toml\napi_version = \"2026-07\"\n```\n\n### Error 8: \"CORS error: Origin not allowed\"\n\n**Cause:** Admin API CORS policy blocking requests\n\n**Fix:**\n1. Ensure requests come from authenticated app context (not localhost)\n2. Use shopify.sessionStorage for session retrieval\n3. Use shopify.rest.api(session) to create authenticated client\n\n### Error 9: \"Cannot read property 'shop' of undefined\"\n\n**Cause:** Session object not populated\n\n**Fix:**\n```typescript\nconst session = await shopify.sessionStorage.loadSession(sessionId);\nif (!session) throw new Error(\"Session not found\");\nconst { shop } = session;\n```\n\n### Error 10: \"Database migration pending\"\n\n**Cause:** Schema changes not applied\n\n**Fix:**\n```bash\nnpx prisma migrate dev --name \"describe migration\"\nnpm run build\nnpm run dev\n```\n\n## Decision Tree (Command Selection)\n\nUser wants to create a new app? Use: shopify app init --template remix\n\nUser wants to start local dev? Use: npm run dev (includes tunnel auto-setup)\n\nUser wants to generate extension? Options:\n- Admin action: shopify generate extension --type admin_action\n- Checkout UI: shopify generate extension --type checkout_ui\n- Theme extension: shopify generate extension --type theme_app_extension\n- Function: shopify generate extension --type shipping_discount\n\nUser wants to deploy app? Options:\n- First time: shopify app deploy (creates version)\n- Update existing: shopify app deploy --force\n\nUser wants to make version live? Use: shopify app release --version X.Y.Z\n\nUser wants to work with themes? Options:\n- Download: shopify theme pull --theme-id 123456789\n- Upload: shopify theme push\n- Dev server: shopify theme dev --store example.myshopify.com\n\nUser wants to test webhooks? Already running in dev server, use curl or Webhook Tester\n\nUser wants to generate GraphQL types? Use: npm run graphql-codegen\n\nUser wants to troubleshoot? Options:\n- Tunnel broken: shopify app dev --reset\n- DB broken: rm dev.db && npm run dev\n- Auth broken: shopify auth logout && npm run dev\n- Port in use: PORT=3001 npm run dev\n\n## Recipes & Cookbook\n\n### Recipe 1: Scaffold New Shopify App from Scratch\n\nGoal: Create a working Shopify app in 5 minutes\n\n```bash\n# 1. Init with Remix template\nshopify app init --template remix\ncd my-app\n\n# 2. Install deps\nnpm install\n\n# 3. Create .env\ncp .env.example .env\n# Edit .env: add SHOPIFY_API_KEY and SHOPIFY_API_SECRET from Partner dashboard\n\n# 4. Setup database\nnpx prisma migrate dev --name \"init\"\n\n# 5. Start dev server\nnpm run dev\n\n# 6. Open in browser\n# Visit tunnel URL shown in terminal\n```\n\nKey Files Created:\n- shopify.app.toml (app config)\n- app/shopify.server.ts (Shopify setup)\n- app/db.server.ts (DB connection)\n- prisma/schema.prisma (data models)\n- Tunnel automatically created and running\n\n### Recipe 2: Add Admin Action Extension\n\nGoal: Add a \"Bulk Edit\" button to product details page\n\n```bash\n# 1. Generate extension\nshopify generate extension --type admin_action\n\n# 2. When prompted:\n# Extension handle: bulk-edit-products\n# Surface: admin.product-details.action.render\n\n# 3. Generated file: extensions/admin-action/src/index.tsx\n# Edit to include proper API calls\n\n# 4. Add to shopify.app.toml:\n[[app_extensions]]\ntype = \"admin_action\"\nhandle = \"bulk-edit-products\"\nlabel = \"Bulk Edit Products\"\n\n# 5. Deploy\nshopify app deploy\n```\n\n### Recipe 3: Add Checkout UI Extension\n\nGoal: Add upsell prompt after purchase\n\n```bash\n# 1. Generate extension\nshopify generate extension --type checkout_ui\n\n# 2. When prompted:\n# Extension handle: post-purchase-upsell\n# API version: 2026-07 (verify latest stable before use)\n\n# 3. Edit extensions/checkout-ui/src/index.tsx with custom logic\n\n# 4. Add to shopify.app.toml:\n[[app_extensions]]\ntype = \"checkout_ui\"\nhandle = \"post-purchase-upsell\"\n\n# 5. Deploy\nshopify app deploy\n```\n\n### Recipe 4: Add Discount Function\n\nGoal: Apply 10% discount to orders over $100\n\n```bash\n# 1. Generate function\nshopify generate extension --type product_discount\n\n# 2. Edit extensions/product-discount/src/index.js with business logic\n\n# 3. Add to shopify.app.toml:\n[[app_extensions]]\ntype = \"product_discount\"\nhandle = \"min-order-discount\"\n\n# 4. Create function config file:\n# extensions/product-discount/shopify.function.toml:\ntype = \"product_discount\"\napi_version = \"2026-07\"\n\n# 5. Deploy\nshopify app deploy\n```\n\n### Recipe 5: Set Up Webhook Handler for Orders\n\nGoal: Sync orders to external system when created\n\n```bash\n# 1. Add webhook subscription to shopify.server.ts with proper callbacks\n\n# 2. Update shopify.app.toml scopes:\nscopes = \"read_orders,write_inventory\"\n\n# 3. Create webhook handler: app/routes/api/webhooks/orders-create.ts\n# with proper signature validation and external sync logic\n\n# 4. Test with curl or Webhook Tester in Partner dashboard\n\n# 5. Deploy\nshopify app deploy\n```\n\n### Recipe 6: Migrate Config from PHP to Remix\n\nGoal: Move from legacy PHP app to new Remix app\n\n```bash\n# 1. Export old PHP app config\n# In old app, run: php export-config.php > old-config.json\n\n# 2. Create new Remix app\nshopify app init --template remix\n\n# 3. Map config to shopify.app.toml structure\n\n# 4. Map environment variables from old to new format\n\n# 5. Copy and refactor webhook handlers from PHP to TypeScript\n\n# 6. Test thoroughly\nnpm run dev\n\n# 7. Deploy both side-by-side during transition period\n```\n\n---\n\nVersion note: examples were refreshed for Admin API 2026-07. Verify current Shopify CLI and package versions before installation.\n"},"changes":[],"summary":"First saved snapshot. No earlier version is available for comparison.","summary_kind":"deterministic","summary_metadata":{}}