Shopify / shopify-app-js

MIT License
232 stars 91 forks source link

[shopify-app-remix] Allow to add dynamic webhooks in `afterAuth()` #1030

Open zirkelc opened 1 month ago

zirkelc commented 1 month ago

Overview

The shopifyApp() function accepts webhooks on initialization. These webhooks can then be registered in afterAuth():

const shopify = shopifyApp({
  apiKey: 'abcde1234567890',
  // ...etc
  webhooks: {
    APP_UNINSTALLED: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: '/webhooks',
    },
  },
  hooks: {
    afterAuth: async ({session}) => {
      shopify.registerWebhooks({session});
    },
  },
});

This works for static webhook where the callback URL or AWS event source is static and available on initialization. In my case, the webhook callback URLs depend on values that I first receive after authorization when I have a valid session.

In this case, I cannot use the static webhooks object on shopifyApp(). Instead, I create a second shopifyApp() object inside the afterAuth which I use to register the webhooks:

const shopify = shopifyApp({
  apiKey: 'abcde1234567890',
  // ...etc
  webhooks: {
    APP_UNINSTALLED: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: '/webhooks',
    },
  },
  hooks: {
    afterAuth: async ({session}) => {
       // get app id and tenant id from API
       // ...

       // format dynamic callback url
       const callbackUrl = formatCallbackUrl(appId, tenantId);

        const webhookFactory = shopifyApp({
          ...config,
          webhooks: {
            APP_UNINSTALLED: {
              deliveryMethod: DeliveryMethod.Http,
              callbackUrl: callbackUrl,
            },
        });

        const result = await webhookFactory.registerWebhooks({ session });
    },
  },
});

It would be nice if there were another option to add webhooks to the shopifyApp() instance after it was created. For example, the instance could a expose the webhooks object so that we can add additional webhooks in afterAuth() OR the shopify.registerWebhooks() function could accept an optional object with webhooks to register:

const shopify = shopifyApp({
  apiKey: 'abcde1234567890',
  // ...etc
  webhooks: {
    APP_UNINSTALLED: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: '/webhooks',
    },
  },
  hooks: {
    afterAuth: async ({session}) => {
      // Add webhooks to object
      shopify.webhooks = {  
        ...shopify.webhooks,
        APP_UNINSTALLED: {
              deliveryMethod: DeliveryMethod.Http,
              callbackUrl: "dynamic-url",
          },
      };
      shopify.registerWebhooks({session});

      // OR

      // pass additional webhooks to function
      shopify.registerWebhooks({session, webhooks: { 
         APP_UNINSTALLED: {
             deliveryMethod: DeliveryMethod.Http,
             callbackUrl: "dynamic-url",
         }
      }});
    },
  },
});
lizkenyon commented 1 month ago

Hi there 👋

Thank you for this! There are some webhook changes coming down the pipe, and we are looking to make some improvements in this area. I am going to flag this with the rest of the team, and we will be keeping this in consideration!