[{"data":1,"prerenderedAt":1002},["ShallowReactive",2],{"docs-\u002Fshopify":3},{"id":4,"title":5,"body":6,"description":995,"extension":996,"meta":997,"navigation":230,"path":998,"seo":999,"stem":1000,"__hash__":1001},"docs\u002Fdocs\u002Fshopify.md","Shopify Integration",{"type":7,"value":8,"toc":977},"minimark",[9,13,17,20,25,33,39,49,54,60,63,69,71,75,82,129,132,145,152,154,158,165,167,171,174,197,202,434,438,441,458,465,469,480,495,523,527,530,635,639,646,707,709,713,720,731,734,736,740,744,747,840,844,847,906,910,913,973],[10,11,5],"h1",{"id":12},"shopify-integration",[14,15,16],"p",{},"Cachely has first-class support for Shopify. It proxies and caches your Shopify storefront asset URLs and provides a response transformer that rewrites them in your Storefront API responses.",[18,19],"hr",{},[21,22,24],"h2",{"id":23},"supported-url-patterns","Supported URL patterns",[14,26,27,28,32],{},"Shopify serves assets from your store domain under the ",[29,30,31],"code",{},"\u002Fcdn\u002F"," path prefix:",[14,34,35],{},[36,37,38],"strong",{},"Product images and files:",[40,41,46],"pre",{"className":42,"code":44,"language":45},[43],"language-text","https:\u002F\u002Fcdn.shopify.com\u002Fs\u002Ffiles\u002F1\u002F0123\u002F4567\u002F8901\u002Ffiles\u002Fproduct.jpg\nhttps:\u002F\u002Fyour-store.myshopify.com\u002Fcdn\u002Fshop\u002Ffiles\u002Fproduct.jpg\n","text",[29,47,44],{"__ignoreMap":48},"",[14,50,51],{},[36,52,53],{},"With image transformations:",[40,55,58],{"className":56,"code":57,"language":45},[43],"https:\u002F\u002Fcdn.shopify.com\u002Fs\u002Ffiles\u002F1\u002F0123\u002F4567\u002F8901\u002Ffiles\u002Fproduct.jpg?v=123&width=1920\n",[29,59,57],{"__ignoreMap":48},[14,61,62],{},"These are rewritten to:",[40,64,67],{"className":65,"code":66,"language":45},[43],"https:\u002F\u002Fyour-tenant.cmsassets.com\u002Fcdn\u002Fshop\u002Ffiles\u002Fproduct.jpg\n",[29,68,66],{"__ignoreMap":48},[18,70],{},[21,72,74],{"id":73},"tenant-setup","Tenant setup",[14,76,77,78,81],{},"When creating your tenant, select ",[36,79,80],{},"Shopify"," as the CMS type and provide your store domain:",[83,84,85,98],"table",{},[86,87,88],"thead",{},[89,90,91,95],"tr",{},[92,93,94],"th",{},"Field",[92,96,97],{},"Value",[99,100,101,109,119],"tbody",{},[89,102,103,107],{},[104,105,106],"td",{},"CMS",[104,108,80],{},[89,110,111,114],{},[104,112,113],{},"Store domain",[104,115,116],{},[29,117,118],{},"your-store.myshopify.com",[89,120,121,124],{},[104,122,123],{},"Website domain",[104,125,126],{},[29,127,128],{},"your-site.com",[14,130,131],{},"The origin is automatically configured from your store domain:",[133,134,135],"ul",{},[136,137,138,141,142],"li",{},[36,139,140],{},"Origin:"," ",[29,143,144],{},"https:\u002F\u002Fyour-store.myshopify.com",[14,146,147,148,151],{},"You can also use a custom Shopify domain (e.g. ",[29,149,150],{},"shop.your-brand.com",") as the store domain.",[18,153],{},[21,155,157],{"id":156},"bandwidth-pricing","Bandwidth pricing",[14,159,160,161,164],{},"Shopify does not charge for CDN bandwidth, so the CMS bandwidth overage rate for Shopify tenants is ",[36,162,163],{},"$0\u002FGB",". You still benefit from edge caching and the unified asset domain that Cachely provides.",[18,166],{},[21,168,170],{"id":169},"response-transformer","Response transformer",[14,172,173],{},"Install the response transformer to automatically rewrite Shopify asset URLs in your Storefront API responses:",[40,175,179],{"className":176,"code":177,"language":178,"meta":48,"style":48},"language-bash shiki shiki-themes github-dark","npm install @synchronized-studio\u002Fresponse-transformer\n","bash",[29,180,181],{"__ignoreMap":48},[182,183,186,190,194],"span",{"class":184,"line":185},"line",1,[182,187,189],{"class":188},"svObZ","npm",[182,191,193],{"class":192},"sU2Wk"," install",[182,195,196],{"class":192}," @synchronized-studio\u002Fresponse-transformer\n",[198,199,201],"h3",{"id":200},"basic-usage","Basic usage",[40,203,207],{"className":204,"code":205,"language":206,"meta":48,"style":48},"language-typescript shiki shiki-themes github-dark","import { transformShopifyAssetUrls } from \"@synchronized-studio\u002Fresponse-transformer\"\n\nconst response = await fetch(\n  `https:\u002F\u002F${storeDomain}\u002Fapi\u002F2024-01\u002Fgraphql.json`,\n  {\n    method: \"POST\",\n    headers: {\n      \"Content-Type\": \"application\u002Fjson\",\n      \"X-Shopify-Storefront-Access-Token\": storefrontToken\n    },\n    body: JSON.stringify({\n      query: `{ products(first: 10) { edges { node { title featuredImage { url } } } } }`\n    })\n  }\n)\nconst data = await response.json()\n\nconst transformed = transformShopifyAssetUrls(data, {\n  storeDomain: \"your-store.myshopify.com\",\n  cmsAssetsUrl: \"https:\u002F\u002Fyour-tenant.cmsassets.com\"\n})\n","typescript",[29,208,209,225,232,254,269,275,286,292,306,315,321,339,348,354,360,366,387,392,408,419,428],{"__ignoreMap":48},[182,210,211,215,219,222],{"class":184,"line":185},[182,212,214],{"class":213},"snl16","import",[182,216,218],{"class":217},"s95oV"," { transformShopifyAssetUrls } ",[182,220,221],{"class":213},"from",[182,223,224],{"class":192}," \"@synchronized-studio\u002Fresponse-transformer\"\n",[182,226,228],{"class":184,"line":227},2,[182,229,231],{"emptyLinePlaceholder":230},true,"\n",[182,233,235,238,242,245,248,251],{"class":184,"line":234},3,[182,236,237],{"class":213},"const",[182,239,241],{"class":240},"sDLfK"," response",[182,243,244],{"class":213}," =",[182,246,247],{"class":213}," await",[182,249,250],{"class":188}," fetch",[182,252,253],{"class":217},"(\n",[182,255,257,260,263,266],{"class":184,"line":256},4,[182,258,259],{"class":192},"  `https:\u002F\u002F${",[182,261,262],{"class":217},"storeDomain",[182,264,265],{"class":192},"}\u002Fapi\u002F2024-01\u002Fgraphql.json`",[182,267,268],{"class":217},",\n",[182,270,272],{"class":184,"line":271},5,[182,273,274],{"class":217},"  {\n",[182,276,278,281,284],{"class":184,"line":277},6,[182,279,280],{"class":217},"    method: ",[182,282,283],{"class":192},"\"POST\"",[182,285,268],{"class":217},[182,287,289],{"class":184,"line":288},7,[182,290,291],{"class":217},"    headers: {\n",[182,293,295,298,301,304],{"class":184,"line":294},8,[182,296,297],{"class":192},"      \"Content-Type\"",[182,299,300],{"class":217},": ",[182,302,303],{"class":192},"\"application\u002Fjson\"",[182,305,268],{"class":217},[182,307,309,312],{"class":184,"line":308},9,[182,310,311],{"class":192},"      \"X-Shopify-Storefront-Access-Token\"",[182,313,314],{"class":217},": storefrontToken\n",[182,316,318],{"class":184,"line":317},10,[182,319,320],{"class":217},"    },\n",[182,322,324,327,330,333,336],{"class":184,"line":323},11,[182,325,326],{"class":217},"    body: ",[182,328,329],{"class":240},"JSON",[182,331,332],{"class":217},".",[182,334,335],{"class":188},"stringify",[182,337,338],{"class":217},"({\n",[182,340,342,345],{"class":184,"line":341},12,[182,343,344],{"class":217},"      query: ",[182,346,347],{"class":192},"`{ products(first: 10) { edges { node { title featuredImage { url } } } } }`\n",[182,349,351],{"class":184,"line":350},13,[182,352,353],{"class":217},"    })\n",[182,355,357],{"class":184,"line":356},14,[182,358,359],{"class":217},"  }\n",[182,361,363],{"class":184,"line":362},15,[182,364,365],{"class":217},")\n",[182,367,369,371,374,376,378,381,384],{"class":184,"line":368},16,[182,370,237],{"class":213},[182,372,373],{"class":240}," data",[182,375,244],{"class":213},[182,377,247],{"class":213},[182,379,380],{"class":217}," response.",[182,382,383],{"class":188},"json",[182,385,386],{"class":217},"()\n",[182,388,390],{"class":184,"line":389},17,[182,391,231],{"emptyLinePlaceholder":230},[182,393,395,397,400,402,405],{"class":184,"line":394},18,[182,396,237],{"class":213},[182,398,399],{"class":240}," transformed",[182,401,244],{"class":213},[182,403,404],{"class":188}," transformShopifyAssetUrls",[182,406,407],{"class":217},"(data, {\n",[182,409,411,414,417],{"class":184,"line":410},19,[182,412,413],{"class":217},"  storeDomain: ",[182,415,416],{"class":192},"\"your-store.myshopify.com\"",[182,418,268],{"class":217},[182,420,422,425],{"class":184,"line":421},20,[182,423,424],{"class":217},"  cmsAssetsUrl: ",[182,426,427],{"class":192},"\"https:\u002F\u002Fyour-tenant.cmsassets.com\"\n",[182,429,431],{"class":184,"line":430},21,[182,432,433],{"class":217},"})\n",[198,435,437],{"id":436},"what-gets-transformed","What gets transformed",[14,439,440],{},"The transformer processes the entire JSON response and rewrites all matching URLs. This includes:",[133,442,443,446,449,452,455],{},[136,444,445],{},"Product images",[136,447,448],{},"Collection images",[136,450,451],{},"Variant images",[136,453,454],{},"Metafield file references",[136,456,457],{},"Any URL matching your Shopify store domain's CDN paths",[14,459,460,461,464],{},"Query parameters (like Shopify's ",[29,462,463],{},"?width=800&height=600",") are stripped during rewriting.",[198,466,468],{"id":467},"using-an-environment-variable","Using an environment variable",[14,470,471,472,475,476,479],{},"Set ",[29,473,474],{},"CMS_ASSETS_URL"," in your environment and omit the ",[29,477,478],{},"cmsAssetsUrl"," option:",[40,481,483],{"className":176,"code":482,"language":178,"meta":48,"style":48},"CMS_ASSETS_URL=https:\u002F\u002Fyour-tenant.cmsassets.com\n",[29,484,485],{"__ignoreMap":48},[182,486,487,489,492],{"class":184,"line":185},[182,488,474],{"class":217},[182,490,491],{"class":213},"=",[182,493,494],{"class":192},"https:\u002F\u002Fyour-tenant.cmsassets.com\n",[40,496,498],{"className":204,"code":497,"language":206,"meta":48,"style":48},"const transformed = transformShopifyAssetUrls(data, {\n  storeDomain: \"your-store.myshopify.com\"\n})\n",[29,499,500,512,519],{"__ignoreMap":48},[182,501,502,504,506,508,510],{"class":184,"line":185},[182,503,237],{"class":213},[182,505,399],{"class":240},[182,507,244],{"class":213},[182,509,404],{"class":188},[182,511,407],{"class":217},[182,513,514,516],{"class":184,"line":227},[182,515,413],{"class":217},[182,517,518],{"class":192},"\"your-store.myshopify.com\"\n",[182,520,521],{"class":184,"line":234},[182,522,433],{"class":217},[198,524,526],{"id":525},"nuxt-ssr-integration","Nuxt \u002F SSR integration",[14,528,529],{},"For Nuxt or other SSR frameworks, wrap your Shopify Storefront API calls:",[40,531,533],{"className":204,"code":532,"language":206,"meta":48,"style":48},"\u002F\u002F composables\u002FuseShopifyData.ts\nimport { transformShopifyAssetUrls } from \"@synchronized-studio\u002Fresponse-transformer\"\n\nexport async function useShopifyProducts() {\n  const data = await shopifyClient.request(productsQuery)\n\n  return transformShopifyAssetUrls(data, {\n    storeDomain: \"your-store.myshopify.com\",\n    cmsAssetsUrl: useRuntimeConfig().public.cmsAssetsUrl\n  })\n}\n",[29,534,535,541,551,555,572,592,596,605,614,625,630],{"__ignoreMap":48},[182,536,537],{"class":184,"line":185},[182,538,540],{"class":539},"sAwPA","\u002F\u002F composables\u002FuseShopifyData.ts\n",[182,542,543,545,547,549],{"class":184,"line":227},[182,544,214],{"class":213},[182,546,218],{"class":217},[182,548,221],{"class":213},[182,550,224],{"class":192},[182,552,553],{"class":184,"line":234},[182,554,231],{"emptyLinePlaceholder":230},[182,556,557,560,563,566,569],{"class":184,"line":256},[182,558,559],{"class":213},"export",[182,561,562],{"class":213}," async",[182,564,565],{"class":213}," function",[182,567,568],{"class":188}," useShopifyProducts",[182,570,571],{"class":217},"() {\n",[182,573,574,577,579,581,583,586,589],{"class":184,"line":271},[182,575,576],{"class":213},"  const",[182,578,373],{"class":240},[182,580,244],{"class":213},[182,582,247],{"class":213},[182,584,585],{"class":217}," shopifyClient.",[182,587,588],{"class":188},"request",[182,590,591],{"class":217},"(productsQuery)\n",[182,593,594],{"class":184,"line":277},[182,595,231],{"emptyLinePlaceholder":230},[182,597,598,601,603],{"class":184,"line":288},[182,599,600],{"class":213},"  return",[182,602,404],{"class":188},[182,604,407],{"class":217},[182,606,607,610,612],{"class":184,"line":294},[182,608,609],{"class":217},"    storeDomain: ",[182,611,416],{"class":192},[182,613,268],{"class":217},[182,615,616,619,622],{"class":184,"line":308},[182,617,618],{"class":217},"    cmsAssetsUrl: ",[182,620,621],{"class":188},"useRuntimeConfig",[182,623,624],{"class":217},"().public.cmsAssetsUrl\n",[182,626,627],{"class":184,"line":317},[182,628,629],{"class":217},"  })\n",[182,631,632],{"class":184,"line":323},[182,633,634],{"class":217},"}\n",[198,636,638],{"id":637},"using-the-generic-transformer","Using the generic transformer",[14,640,641,642,645],{},"You can also use the unified ",[29,643,644],{},"transformCmsAssetUrls"," function:",[40,647,649],{"className":204,"code":648,"language":206,"meta":48,"style":48},"import { transformCmsAssetUrls } from \"@synchronized-studio\u002Fresponse-transformer\"\n\nconst transformed = transformCmsAssetUrls(data, {\n  cms: \"shopify\",\n  storeDomain: \"your-store.myshopify.com\",\n  cmsAssetsUrl: \"https:\u002F\u002Fyour-tenant.cmsassets.com\"\n})\n",[29,650,651,662,666,679,689,697,703],{"__ignoreMap":48},[182,652,653,655,658,660],{"class":184,"line":185},[182,654,214],{"class":213},[182,656,657],{"class":217}," { transformCmsAssetUrls } ",[182,659,221],{"class":213},[182,661,224],{"class":192},[182,663,664],{"class":184,"line":227},[182,665,231],{"emptyLinePlaceholder":230},[182,667,668,670,672,674,677],{"class":184,"line":234},[182,669,237],{"class":213},[182,671,399],{"class":240},[182,673,244],{"class":213},[182,675,676],{"class":188}," transformCmsAssetUrls",[182,678,407],{"class":217},[182,680,681,684,687],{"class":184,"line":256},[182,682,683],{"class":217},"  cms: ",[182,685,686],{"class":192},"\"shopify\"",[182,688,268],{"class":217},[182,690,691,693,695],{"class":184,"line":271},[182,692,413],{"class":217},[182,694,416],{"class":192},[182,696,268],{"class":217},[182,698,699,701],{"class":184,"line":277},[182,700,424],{"class":217},[182,702,427],{"class":192},[182,704,705],{"class":184,"line":288},[182,706,433],{"class":217},[18,708],{},[21,710,712],{"id":711},"shopify-image-transformations","Shopify Image Transformations",[14,714,715,716,719],{},"Shopify's CDN supports on-the-fly image transformations via query parameters like ",[29,717,718],{},"?width=800&height=600&crop=center",". When assets are proxied through Cachely:",[133,721,722,725,728],{},[136,723,724],{},"The edge cache strips query parameters from the cache key for image types",[136,726,727],{},"This means all image transformation variants share the same cache entry",[136,729,730],{},"If you rely on Shopify's CDN transformations, the first cached version will be served for all variants",[14,732,733],{},"If you need different image sizes, apply transformations on the client side or serve each size from a different path.",[18,735],{},[21,737,739],{"id":738},"advanced-options","Advanced options",[198,741,743],{"id":742},"custom-transformers","Custom transformers",[14,745,746],{},"Add additional transformers that run after the default Shopify ones:",[40,748,750],{"className":204,"code":749,"language":206,"meta":48,"style":48},"const transformed = transformShopifyAssetUrls(data, {\n  storeDomain: \"your-store.myshopify.com\",\n  transformers: [\n    (jsonStr, { base }) => {\n      return jsonStr.replaceAll(\"old-pattern\", \"new-pattern\")\n    }\n  ]\n})\n",[29,751,752,764,772,777,801,826,831,836],{"__ignoreMap":48},[182,753,754,756,758,760,762],{"class":184,"line":185},[182,755,237],{"class":213},[182,757,399],{"class":240},[182,759,244],{"class":213},[182,761,404],{"class":188},[182,763,407],{"class":217},[182,765,766,768,770],{"class":184,"line":227},[182,767,413],{"class":217},[182,769,416],{"class":192},[182,771,268],{"class":217},[182,773,774],{"class":184,"line":234},[182,775,776],{"class":217},"  transformers: [\n",[182,778,779,782,786,789,792,795,798],{"class":184,"line":256},[182,780,781],{"class":217},"    (",[182,783,785],{"class":784},"s9osk","jsonStr",[182,787,788],{"class":217},", { ",[182,790,791],{"class":784},"base",[182,793,794],{"class":217}," }) ",[182,796,797],{"class":213},"=>",[182,799,800],{"class":217}," {\n",[182,802,803,806,809,812,815,818,821,824],{"class":184,"line":271},[182,804,805],{"class":213},"      return",[182,807,808],{"class":217}," jsonStr.",[182,810,811],{"class":188},"replaceAll",[182,813,814],{"class":217},"(",[182,816,817],{"class":192},"\"old-pattern\"",[182,819,820],{"class":217},", ",[182,822,823],{"class":192},"\"new-pattern\"",[182,825,365],{"class":217},[182,827,828],{"class":184,"line":277},[182,829,830],{"class":217},"    }\n",[182,832,833],{"class":184,"line":288},[182,834,835],{"class":217},"  ]\n",[182,837,838],{"class":184,"line":294},[182,839,433],{"class":217},[198,841,843],{"id":842},"post-transform-hook","Post-transform hook",[14,845,846],{},"Run a function on the parsed result after all URL replacements:",[40,848,850],{"className":204,"code":849,"language":206,"meta":48,"style":48},"const transformed = transformShopifyAssetUrls(data, {\n  storeDomain: \"your-store.myshopify.com\",\n  postTransform: (data) => {\n    return data\n  }\n})\n",[29,851,852,864,872,890,898,902],{"__ignoreMap":48},[182,853,854,856,858,860,862],{"class":184,"line":185},[182,855,237],{"class":213},[182,857,399],{"class":240},[182,859,244],{"class":213},[182,861,404],{"class":188},[182,863,407],{"class":217},[182,865,866,868,870],{"class":184,"line":227},[182,867,413],{"class":217},[182,869,416],{"class":192},[182,871,268],{"class":217},[182,873,874,877,880,883,886,888],{"class":184,"line":234},[182,875,876],{"class":188},"  postTransform",[182,878,879],{"class":217},": (",[182,881,882],{"class":784},"data",[182,884,885],{"class":217},") ",[182,887,797],{"class":213},[182,889,800],{"class":217},[182,891,892,895],{"class":184,"line":256},[182,893,894],{"class":213},"    return",[182,896,897],{"class":217}," data\n",[182,899,900],{"class":184,"line":271},[182,901,359],{"class":217},[182,903,904],{"class":184,"line":277},[182,905,433],{"class":217},[198,907,909],{"id":908},"error-handling","Error handling",[14,911,912],{},"By default, transform errors are logged as warnings and the original data is returned unchanged. You can provide a custom error handler:",[40,914,916],{"className":204,"code":915,"language":206,"meta":48,"style":48},"const transformed = transformShopifyAssetUrls(data, {\n  storeDomain: \"your-store.myshopify.com\",\n  onError: (error) => {\n    Sentry.captureException(error)\n  }\n})\n",[29,917,918,930,938,954,965,969],{"__ignoreMap":48},[182,919,920,922,924,926,928],{"class":184,"line":185},[182,921,237],{"class":213},[182,923,399],{"class":240},[182,925,244],{"class":213},[182,927,404],{"class":188},[182,929,407],{"class":217},[182,931,932,934,936],{"class":184,"line":227},[182,933,413],{"class":217},[182,935,416],{"class":192},[182,937,268],{"class":217},[182,939,940,943,945,948,950,952],{"class":184,"line":234},[182,941,942],{"class":188},"  onError",[182,944,879],{"class":217},[182,946,947],{"class":784},"error",[182,949,885],{"class":217},[182,951,797],{"class":213},[182,953,800],{"class":217},[182,955,956,959,962],{"class":184,"line":256},[182,957,958],{"class":217},"    Sentry.",[182,960,961],{"class":188},"captureException",[182,963,964],{"class":217},"(error)\n",[182,966,967],{"class":184,"line":271},[182,968,359],{"class":217},[182,970,971],{"class":184,"line":277},[182,972,433],{"class":217},[974,975,976],"style",{},"html pre.shiki code .svObZ, html code.shiki .svObZ{--shiki-default:#B392F0}html pre.shiki code .sU2Wk, html code.shiki .sU2Wk{--shiki-default:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html pre.shiki code .snl16, html code.shiki .snl16{--shiki-default:#F97583}html pre.shiki code .s95oV, html code.shiki .s95oV{--shiki-default:#E1E4E8}html pre.shiki code .sDLfK, html code.shiki .sDLfK{--shiki-default:#79B8FF}html pre.shiki code .sAwPA, html code.shiki .sAwPA{--shiki-default:#6A737D}html pre.shiki code .s9osk, html code.shiki .s9osk{--shiki-default:#FFAB70}",{"title":48,"searchDepth":227,"depth":227,"links":978},[979,980,981,982,989,990],{"id":23,"depth":227,"text":24},{"id":73,"depth":227,"text":74},{"id":156,"depth":227,"text":157},{"id":169,"depth":227,"text":170,"children":983},[984,985,986,987,988],{"id":200,"depth":234,"text":201},{"id":436,"depth":234,"text":437},{"id":467,"depth":234,"text":468},{"id":525,"depth":234,"text":526},{"id":637,"depth":234,"text":638},{"id":711,"depth":227,"text":712},{"id":738,"depth":227,"text":739,"children":991},[992,993,994],{"id":742,"depth":234,"text":743},{"id":842,"depth":234,"text":843},{"id":908,"depth":234,"text":909},"Serve Shopify product images and media through your own edge domain with global caching, bandwidth tracking, and bot protection.","md",{},"\u002Fdocs\u002Fshopify",{"title":5,"description":995},"docs\u002Fshopify","cqrcb7IJAfntRrT88u_j7BmB_6TdIFMnElakj4-oXhI",1777579477903]