In scripttag js file how to set dynamic API url in Public App?

Topic summary

A developer is building a Shopify public app using the PHP App Template and has created a script tag for WhatsApp cart sharing functionality. The script is hosted externally (separate from the app folder) and currently contains hardcoded API URLs pointing to a Cloudflare tunnel endpoint.

Core Issue:
The developer needs to dynamically set the API URL in the script tag file instead of manually updating the Cloudflare tunnel URL each time during development.

Technical Context:

  • The app runs locally using npm run dev with a Cloudflare tunnel for testing
  • The script tag JS file fetches data from endpoints like /api/get-cart-url and /api/get-saved-form-data
  • Currently uses hardcoded URLs (e.g., https://spiritual-perspective-lawyer-carrier.trycloudflare.com)

Response:
A community member requested clarification on the logical sequence and noted there’s no way to dynamically use values inside a JS file or inline JS snippet. They also warned about the security risk of publicly exposing an API key in the posted code.

Status: The discussion remains open with the developer seeking a solution for dynamic URL configuration in externally-hosted script tags.

Summarized with AI on November 19. AI used: claude-sonnet-4-5-20250929.

hello, i have created Shopify public app using Shopify PHP App Template,

below is ,y Script tag js Code,

fetch('https://spiritual-perspective-lawyer-carrier.trycloudflare.com/api/get-cart-url')
  .then(response => response.json())
  .then(cartData => {
    // Fetch the form data
    return fetch('https://spiritual-perspective-lawyer-carrier.trycloudflare.com/api/get-saved-form-data')
      .then(response => response.json())
      .then(formData => {
        // Access the form values from the response
        const selected  = formData[0].wp_cart_enable_disable;
        const blt       = formData[0].wp_button_label_text;
        const cmwsl     = formData[0].wp_cart_mesage_on_link;
        const selected1 = formData[0].wp_button_position;        
        const bgat      = formData[0].bitly_access_token;        

        var checkoutButton = document.querySelector('[name="checkout"]');

        if (selected === 'yes') {          
        // Share the cart URL with the product          
        fetch('https://ncodeshop.myshopify.com/cart.js')
          .then(response => response.json())
          .then(data => {          
            // Create the WhatsApp button
            var customButton = document.createElement('button');
            customButton.innerText =  blt;
            customButton.style.backgroundColor = '#006E52';
            customButton.classList.add('whatsapp-button');

            // Add CSS styles to the button
            customButton.addEventListener('mouseenter', function() {
              customButton.style.color = 'white'; // Change text color to white
            });
                        
            customButton.addEventListener('click', function() {
              const cartToken = data.token;
              const items = data.items; // Array of cart items

              // Step 2: Construct the shared cart URL
              let sharedCartUrl = `https://ncodeshop.myshopify.com/cart/`;

              // Iterate over the cart items and append variant IDs with quantities to the URL
              for (let i = 0; i < items.length; i++) {
                const variantId = items[i].variant_id;
                const quantity = items[i].quantity;
                const encodedVariantId = encodeURIComponent(variantId);

                // Append the variant ID and quantity to the URL
                sharedCartUrl += `${encodedVariantId}:${quantity},`;
              }

              // Remove the trailing comma from the URL
              sharedCartUrl = sharedCartUrl.slice(0, -1);

              // Append the remaining part of the URL
              sharedCartUrl += `?storefront=true`;

              // Step 3: Share the URL with the user (e.g., display as a link or button)
              console.log('Shared Cart URL:', sharedCartUrl);

              // Access the cart data from the previous fetch call
              console.log('Cart Data:', cartData);

              var cartItems = cartData || [];
              console.log('Cart Items:', cartItems);

              var cartUrl = cartData.cartUrl;

              var message = cmwsl + ' \nCart URL: ' + sharedCartUrl;

              // Generate a short link using the Bitly API
              const accessToken = bgat;
              console.log('Bitly Token:', accessToken);
              const longUrl     = sharedCartUrl;
              console.log('Long URL:',longUrl);

            if (accessToken && accessToken.trim() !== '') {
              event.preventDefault();
              fetch('https://api-ssl.bitly.com/v4/shorten', {
                method: 'POST',
                headers: {
                  'Authorization': `Bearer ${accessToken}`,
                  'Content-Type' : 'application/json',
                  'Access-Control-Allow-Headers' : '*',
                },
                //body: JSON.stringify({ long_url: longUrl })
                body: JSON.stringify({ "long_url": longUrl, "domain": "bit.ly" })
              })
                //.then(response => response.json())
                .then(response => {
                  if (response.ok) {
                    return response.json();
                  } else {
                    throw new Error('Bitly API request failed.');
                  }
                })
                .then(bitlyResponse => {
                  if (bitlyResponse && bitlyResponse.link) {
                    var shortLink = bitlyResponse.link;
                    console.log('Short Link:', shortLink);

                    // Use the shortLink as needed (e.g., display or share)
                    var wpurl = cmwsl + ' \nCart URL: ' + shortLink;
                    var whatsappUrl = 'https://api.whatsapp.com/send?text=' + encodeURIComponent(wpurl) + '&redirect=false';
                    window.open(whatsappUrl, '_blank');
                  } else {
                    console.error('Error generating short link:', bitlyResponse);
                    alert('Token is not valid.'); // Display an alert when the token is not valid
                  }
                })
                .catch(error => {
                  console.error('Error generating short link:', error);  
                  alert('Wrong Bitly Token or You’ve used all of your Bitlinks this month.'); // Display an alert when the Bitly API request fails
                
                });
            } else {
              // If accessToken is empty, use WhatsApp API without Bitly
              var wpurl = cmwsl + ' \nCart URL: ' + sharedCartUrl;
              var whatsappUrl = 'https://api.whatsapp.com/send?text=' + encodeURIComponent(wpurl) + '&redirect=false';
              window.open(whatsappUrl, '_blank');
            }

            });

            // Append the WhatsApp button to the parent container of the checkout button
            //checkoutButton.parentNode.appendChild(whatsappButton);
            checkoutButton.parentNode.insertBefore(customButton, checkoutButton);
          })
          .catch(error => {
            console.error('Error fetching cart URL:', error);
          });

          // Dynamically inject the CSS styles into the document head
          var style = document.createElement('style');
          style.innerHTML = `
            @media screen and (max-width: 767px) {
              button.whatsapp-button {
                width: 100%;
                margin-bottom: 10px;
              }
            }
          `;
          document.head.appendChild(style);
        }
      });
  })
  .catch(error => {
    console.error('Error fetching data:', error);
  });

in above code there is fetch API,
fetch(‘https://spiritual-perspective-lawyer-carrier.trycloudflare.com/api/get-cart-url’)
return fetch(‘https://spiritual-perspective-lawyer-carrier.trycloudflare.com/api/get-saved-form-data’)

that comes from the when run command “npm run dev”,
at that time this URL generate using cloudflare tunnel, and what I do here,

I copy that generated URL and paste in to the scripttag js,

now I want to use dynamic that URL in script tag js file,

how can I do that??

here is my Controller code

first();
    if (!$store) {           
        return response()->json(['error' => 'Store information not found'], 404);
    }       
    $shopDomain  = $store->shop;
    $apiKey      = '47ff03ffb186ff9c5a9aeb5f1b1ea85345';
    $accessToken = $store->access_token;
   
    // Set the script tag properties
    $scriptTag = [
        'event' => 'onload',
        'src'   => 'https://www.xuz..in/upload/wpcartbutton.js'
    ];
    
    // Create the script tag using the Shopify API        
    $response = Http::withHeaders([
        'X-Shopify-Access-Token' => $accessToken,
        'Content-Type'           => 'application/json'
    ])->post("https://{$shopDomain}/admin/api/2023-04/script_tags.json", [
        'script_tag'            => $scriptTag,
        'display_scope'         => 'cart'

    ]);
    
    return $response->json();    
}

here ‘src’ => ‘https://www.xuz..in/upload/wpcartbutton.js
wpcartbutton.js is a script tag creation file.

below is my ScrtiptagCreator.jsx file code

import React, { useEffect, useState } from 'react';
import { Button, Card, Page } from '@shopify/polaris';
import { useAppBridge } from '@shopify/app-bridge-react';
import { Redirect } from '@shopify/app-bridge/actions';
import { TitleBar } from "@shopify/app-bridge-react";

export function ScriptTagCreator() {
  const app = useAppBridge();
  const [scriptTagCreated, setScriptTagCreated] = useState(false);

  useEffect(() => {
    async function createScriptTag() {
      // Check if the script tag is already created
      if (scriptTagCreated) {
        console.log('Script tag already created');
        return;
      }

      const response = await fetch('/api/create-script-tag', {
        method: 'POST',
        headers: {
          'Content-Type'    : 'application/json',
          'X-Requested-With': 'XMLHttpRequest', 
        },
        body: JSON.stringify({
          shop: app.shopOrigin,
        }),
      });

      const data = await response.json();
      console.log(data);
    }

   // Check if we are on the cart page before creating the script tag
    if (window.location.pathname === '/cart') {
      createScriptTag();
    }
  }, []);

  return (
    
  );
}

Hello @rushikesh93 ,

I need some more detail to assist you. Can you describe shortly the logical sequence of actions that should occur in your code? So I can better understand the dynamic URL and why you need it. On the other hand, there is no way to dynamically use something inside the js-file or inside the inline js-snippet.

Also, please pay attention to the fact that you have publicly posted your $apiKey in the code, you should avoid doing it.

Best regards,
Anastasia

Hello, @Stacy_Zhuk thanks for your valuable response.
what happened here, I have created the script tag, the code is available in the above question.

and that script is outside of my app folder, which means I have hosted in some other hosting,

when my app is run using the “npm run dev” command, and one cloudflare tunnel, and that created tunnel URL, I’m testing my Shopify app.

now my question is, in that scipttag file, how to send my dynamic cloudflare URL,? so I don’t put cloudflare url manually .