How to delete files when the app is uninstalled?

I have created an app, and with the admin’s panel, we can add any section to the theme. However, I want to ensure that when the app is uninstalled, any section and template files that were added also get deleted at the time of uninstallation.

My app is built using a Node.js template, and it fetches data from a GraphQL API.

Here is my code for deleting the theme file:`app.post(‘/api/theme/fileDelete’, async (req, res) => {
try {
// Initialize Shopify client
const client = new shopify.api.clients.Graphql({
session: res.locals.shopify.session,
});

const themeId = "gid://shopify/OnlineStoreTheme/146551537909"; // specify your theme ID
const filesToDelete = ["templates/index3.liquid"]; // specify the files to delete

// Set up GraphQL mutation to delete theme files
let mutation = `mutation($themeId: ID!, $files: [String!]!) {
  themeFilesDelete(themeId: $themeId, files: $files) {
    deletedThemeFiles {
      filename
    }
    userErrors {
      field
      message
    }
  }
}`;

// Send the request
const response = await client.request(mutation, { variables: { themeId, files: filesToDelete } });

// Log the result and send a response
console.log("Files deleted:", response.data);

res.status(200).send({message: "Theme files deleted"});

} catch (error) {
console.error(“Error deleting theme files on uninstall:”, error);
res.status(500).send({message: error});
}
});

//or

const themeFileDelete = async (req,res) => {
try {
// Initialize Shopify client
const client = new shopify.api.clients.Graphql({
session: res.locals.shopify.session,
});

const themeId = "gid://shopify/OnlineStoreTheme/146551537909"; // specify your theme ID
const filesToDelete = ["templates/index3.liquid"]; // specify the files to delete

// Set up GraphQL mutation to delete theme files
let mutation = `mutation($themeId: ID!, $files: [String!]!) {
  themeFilesDelete(themeId: $themeId, files: $files) {
    deletedThemeFiles {
      filename
    }
    userErrors {
      field
      message
    }
  }
}`;

// Send the request
const response = await client.request(mutation, { variables: { themeId, files: filesToDelete } });

// Log the result and send a response
console.log("Files deleted:", response.data);

res.status(200).send({message:"delete theme file"})
// Optionally, you can also send a confirmation response back to the shop (if needed)

} catch (error) {
console.error(“Error deleting theme files on uninstall:”, error);
res.status(500).send({message:error})

}
};

export default themeFileDelete;`

I have also set up a webhook for when the app is uninstalled, and it is working fine. However, I want the theme file deletion API to run only when the uninstall webhook is triggered.

Note: I have created the uninstall webhook in the privacy.js file, and I need to figure out how to run the theme deletion function from index.js when the uninstall webhook is triggered.

Here’s how my webhook setup looks:

import { DeliveryMethod } from “@shopify/shopify-api”;
import themeFileDelete from ‘./index.js’
/**

  • @type {{[key: string]: import(“@shopify/shopify-api”).WebhookHandler}}
    /
    export default {
    /
    *
    • Customers can request their data from a store owner. When this happens,
    • Shopify invokes this privacy webhook.
    • Privacy law compliance
      */
      CUSTOMERS_DATA_REQUEST: {
      deliveryMethod: DeliveryMethod.Http,
      callbackUrl: “/api/webhooks”,
      callback: async (topic, shop, body, webhookId) => {
      const payload = JSON.parse(body);
      // console.log(payload,“payloadpayload”)
      // Payload has the following shape:
      // {
      // “shop_id”: 954889,
      // “shop_domain”: “{shop}.myshopify.com”,
      // “orders_requested”: [
      // 299938,
      // 280263,
      // 220458
      // ],
      // “customer”: {
      // “id”: 191167,
      // “email”: “john@example.com”,
      // “phone”: “555-625-1199”
      // },
      // “data_request”: {
      // “id”: 9999
      // }
      // }
      },
      },

/**

  • Store owners can request that data is deleted on behalf of a customer. When
  • this happens, Shopify invokes this privacy webhook.
  • Privacy law compliance
    */
    CUSTOMERS_REDACT: {
    deliveryMethod: DeliveryMethod.Http,
    callbackUrl: “/api/webhooks”,
    callback: async (topic, shop, body, webhookId) => {
    const payload = JSON.parse(body);
    // console.log(payload,“payload2”)
    // Payload has the following shape:
    // {
    // “shop_id”: 954889,
    // “shop_domain”: “{shop}.myshopify.com”,
    // “customer”: {
    // “id”: 191167,
    // “email”: “john@example.com”,
    // “phone”: “555-625-1199”
    // },
    // “orders_to_redact”: [
    // 299938,
    // 280263,
    // 220458
    // ]
    // }
    },
    },

/**

  • 48 hours after a store owner uninstalls your app, Shopify invokes this
  • privacy webhook.
  • Privacy law compliance
    */
    SHOP_REDACT: {
    deliveryMethod: DeliveryMethod.Http,
    callbackUrl: “/api/webhooks”,
    callback: async (topic, shop, body, webhookId) => {
    const payload = JSON.parse(body);
    // console.log(payload,“payload3”)
    // console.log(payload,“appuninstall detata”)
    // Payload has the following shape:
    // {
    // “shop_id”: 954889,
    // “shop_domain”: “{shop}.myshopify.com”
    // }
    },
    },

/**

  • Webhook for when the app is uninstalled
    */
    APP_UNINSTALLED: {
    deliveryMethod: DeliveryMethod.Http,
    callbackUrl: “/api/webhooks”,
    callback: async (topic, shop, body, webhookId) => {

    const payload = JSON.parse(body);

    console.log(payload,“--------------Payload”)
    console.log(topic,“--------------topic”)
    console.log(shop,“--------------shop”)
    console.log(webhookId,“--------------webhookId”)

    await themeFileDelete()
    }

}

};

:wave: Hi @Rohit_Maurya when you receive the APP_UNINSTALLED webhook from Shopify, your app will already have been removed from the shop for 48 hours. This means that your access tokens for this shop will already have been revoked, so you will not be able to call the API to perform any actions on the shop.

Hi @Dave-Shopify ,
I have created an app, and in this app, I have added an option to include sections. For example, when a customer installs my app, they can add a Shopify section to their store through the admin panel of my app. However, when they uninstall the app, all the sections added by my app should be removed. I want to achieve this functionality.

Can you tell us how we can do this?

Hi @Rohit_Maurya,
I recommend you build your app as a Theme App Extension, providing App Blocks. When a merchant uninstalls an any app that provides App Blocks, those blocks will automatically no longer render in the editor or on the storefront and configuration data will be automatically cleaned up when the page is next saved.

Outside of this flow, as soon as the merchant has uninstalled your app access tokens will be immediately revoked, preventing API access.

Hi @Nic-Shopify
I am using the ‘App Block’ extension to create my app, and I need to add a payment option as well. How can I do that? I want to know if there is a solution where the admin can add a section to the app, and this section should only be visible in the theme editor after being purchased. Is something like this possible?