Accessing attachments from Custom Widget builder

Hey everyone,

I recently got a question about how to access an image from an attachment field in Grist and display it in a custom widget. I’ll try to summarize it here for anyone interested.

To download an attachment Grist exposes an API endpoint available here.

Unfortunately, the Grist API isn’t directly accessible from the browser for security reasons, but there’s a special endpoint you can use to get a short-lived access token. This token lets you safely access attachments within your custom widget (even those built using Custom widget builder).

I’ve created a demo you can check out here: Attachments in builder - Grist

Here’s how to do it:

  1. Get the Attachment ID
    The column in Grist stores a list of attachment IDs. You can grab the first one with record.ColumnName[0].
  2. Get the Access Token
    Use grist.docApi.getAccessToken() to get a short-lived token and the base URL for the document.
  3. Build the URL
    Join the base URL, attachment ID, and token to form the full URL for the attachment data.
  4. Display the Image
    Use the URL in an <img> tag to display the image or use it in any other way you like.

Here is a very simple code that demonstrate it (part of the example above):

  grist.ready({ requiredAccess: 'full' });

  const imageEl = document.getElementById("image");

  let token = "";
  let baseUrl = "";

  function updateImageUrl(id) {
    const url = `${baseUrl}/attachments/${id}/download?auth=${token}`;
    imageEl.src = url; // Set the URL as the image source
  }

  // Get the access token and base URL
  grist.docApi.getAccessToken({ readOnly: true }).then(response => {
    token = response.token;
    baseUrl = response.baseUrl;
  });

  // Listen for changes to the current record
  grist.onRecord(record => {
    const attachmentId = record.Image?.[0]; // Get the first attachment ID
    if (attachmentId) {
      updateImageUrl(attachmentId);
    }
  });
3 Likes

Can I access an attached Excel file with this… and extract data?

Yes you can, you can access the attachment regardless of the type of file it is. It’s just a question of what you do with it. A quick and very rough version that works for accessing attached Excel files by using the sheetjs library can be found here Attachments in builder Excel eample - Grist

Hi Jarek,
Thank you for this post!
I have a question: is there any way to do the “classic” API call from a custom widget, and get a link from that to send the link by email and allow the attachment download?

Indeed, I would like to create a custom widget that allows users to visualize and send personalized emails, with the ability to add an attachment as an url.

I successfully did it using your way to build a link, until I correctly read your post ( :upside_down_face:) and realized that:

  • it was a short-lived access token, so the link wouldn’t be useful after a short time
  • sending the link to someone would allow them to access other attachments…

This is what I did so far (all credits to @Amandine_Dugrain for the css :))

And the email is sending well:

The final use case is the ability to send invoices by email.

1 Like

there is a saying in my language… “I give you a hand, you want the whole arm”

lol…

You are allowing us to access images through custom widgets. But how about ATTACHING files? :smiley:

Downloading them is already well-supported with grist.docApi.getAccessToken(), which gives a short lived token and works great for displaying images or files in a secure way.

But… to upload attachments, the only available method seems to be the public API endpoint (POST /docs/{docId}/attachments) that requires a full API key in the Authorization header.

Doesn´t this make it unsafe to implement uploads directly from a widget, since exposing an API key in client side code is a serious security risk?

Would it be possible to provide a secure client-side method for uploading attachments through the grist-plugin-api.js, similar to how getAccessToken() works for downloads?

Something like
grist.docApi.uploadAttachment(file).then(attachmentId => {
// Update the record with the new attachment ID
});

1 Like

Hi Jarek. I was able to use the method to display images.

But I am failing in obtaining the TYPE of attachment. So I can treat differently the file if it’s an Excel, or an image, etc.

What API call displays the file name and extension?

Hi @Rogerio_Penna,

I will check and get back to you with it. From what I remember there were some security concerns around those types, but I don’t remember exactly.