Attach_to in JS documentation?

Hi All,

In a backend plugin that creates a file somehow i end up creating both the data type and data id in the Attached to field. Anyone who knows where i find some documentation

if (thingToConnect && typeof thingToConnect === "object") {
    // Case 1: The pointer structure
    if (thingToConnect._pointer &&
        thingToConnect._pointer._id &&
        thingToConnect._pointer._type) {
      attachType = thingToConnect._pointer._type;
      attachID   = thingToConnect._pointer._id;

    // Case 2: The direct { datatype, unique_id } structure
    } else if (thingToConnect.datatype && thingToConnect.unique_id) {
      attachType = thingToConnect.datatype;
      attachID   = thingToConnect.unique_id;
    }
  if (attachType && attachType.startsWith("custom.")) {
    attachType = attachType.replace(/^custom\./, "");
if (attachType && attachID) {
        form.append("attach_to", `${attachType},${attachID}`);
        form.append("private", "true");
        console.log(`Attaching to: ${attachType}, ${attachID}`);
      } else {
        console.warn("No valid Thing detected. The file will be uploaded publicly.");
      }
 
![Scherm­afbeelding 2025-01-29 om 19.52.11|234x134](upload://oDw8iYoFKlBYedZtn3wDWZNdiW6.png)

You don’t need the type, you only need the unique ID of the item you want to attach the file to.

thanks for responding and just to be sure, how are files connected to the relevant table?

Scherm­afbeelding 2025-01-29 om 20.38.20
is what i keep getting

your plugin should return a file url with after you uploaded the file with your plugin. This is what you need to use and do an action like make change to a thing to link the file with the item.

The plugin is creating a file server-side (.zip). After creating the zip the following steps are performed:

* 7) DETERMINE ATTACH_TO PARAMETERS (Data Type & ID)
   *************************************************************/
  let attachType = null;
  let attachID = null;

  // Check if the Thing exists and has a valid _pointer structure
  if (thingToConnect && typeof thingToConnect === "object" && thingToConnect._pointer) {
    if (thingToConnect._pointer._id && thingToConnect._pointer._type) {
      attachID   = thingToConnect._pointer._id;
      attachType = thingToConnect._pointer._type;
    }
  }

  // Ensure that attachType is properly formatted (remove "custom." if present)
  if (attachType && attachType.startsWith("custom.")) {
    attachType = attachType.replace(/^custom\./, "");
  }

  /*************************************************************
   * 8) DOWNLOAD ALL FILES AND GENERATE THE ZIP
   *************************************************************/
  const allPromises = addToZip(zip, folderData);

  return Promise.all(allPromises)
    .then(() => {
      // When all downloads finish, generate the ZIP buffer
      return zip.generateAsync({ type: "nodebuffer" });
    })
    .then(buffer => {
      /***********************************************************
       * 9) UPLOAD THE ZIP TO BUBBLE'S FILEUPLOAD ENDPOINT
       ***********************************************************/
      const form = new FormData();
      form.append("file", buffer, {
        filename: outputFileName,
        contentType: "application/zip"
      });

      // Attach to Bubble "Thing" if valid
      if (attachType && attachID) {
        form.append("attach_to", `${attachType},${attachID}`);
        form.append("private", "true");
        console.log(`đź”— Attaching to: ${attachType}, ${attachID}`);
      } else {
        console.warn("⚠️ No valid Thing detected. File will be uploaded publicly.");
      }

      // Perform the POST request to fileUploadURL
      return axios.post(fileUploadURL, form, {
        headers: form.getHeaders()
      });
    })
    .then(response => {
      /***********************************************************
       * 10) PARSE THE RESPONSE AND RETURN THE FINAL FILE URL
       ***********************************************************/
      const data = response.data;
      let fileUrl;

      // Bubble can return the URL as a string or as { url: ... }
      if (typeof data === "string") {
        fileUrl = data.trim();
      } else if (data && data.url) {
        fileUrl = data.url;
      } else {
        throw new Error("File upload failed. Unexpected response: " + JSON.stringify(data));
      }

      // Bubble might return protocol-relative URLs 
      if (fileUrl.startsWith("//")) {
        fileUrl = "https:" + fileUrl;
      }

      // Return data so Bubble can store or display it
      return {
        zip_file_url: fileUrl,  // text field
        zip_file: fileUrl       // file field
      };
    })
    .catch(err => {
      // Any error in the chain is caught here
      throw new Error("Error generating ZIP file: " + err.message);
    });
}

After the file is created… you need to upload the file to bubble storage. From your code, you seem to have this part. This step will return an url. you need to use the return function and add a field to store the url that could be use in step after this plugin action (result of step X’s file url for example)

Take note that, if there’s no server side zip plugin actually on plugin marketplace, this because there’s a good reason: You will be limited to very small zip file on server side.

1 Like

thanks for looking into this. The file is uploaded to storage (and private) as part of the flow (works well). The files are fairly small (containing data) and are used in e-mail reporting so front-end is not an option.

1 Like