Skip to content

Attachments & REST

When you need data the plugin API doesn't expose directly — attachment bytes, SQL queries, the document's REST endpoints — the SDK gives you a signed fetch that handles authentication for you.

Access tokens

Grist short-lived access tokens authorize REST calls against the current document. useGrist() exposes:

ts
const token = await w.getAccessToken({ readOnly: true })
// { token: string, baseUrl: string }

Tokens are cached briefly (~45s) and single-flighted per readOnly flag. On 401, the cache clears and the call is retried once. You should rarely need to call getAccessToken directly — prefer w.fetchWithAuth(...).

REST fetch

ts
const response = await w.fetchWithAuth("/api/docs/{docId}/sql", {
  method: "POST",
  body: JSON.stringify({ sql: "SELECT * FROM Tasks LIMIT 10" }),
  headers: { "Content-Type": "application/json" },
})
const data = await response.json()

path may be:

  • An absolute URL (https://...) — used as-is.
  • A path with leading / — resolved against the token's baseUrl.
  • A relative path — treated as /${path}.

For GET / HEAD the token is appended as ?auth=. For other methods it's sent as Authorization: Bearer ….

Pass readOnly: true when calling read-only endpoints; the token is scoped lower and stays valid longer.

ts
await w.fetchWithAuth("/api/docs/.../tables/Tasks/records", { readOnly: true })

Attachments

Grist attachment columns store an array of file refs. The SDK handles extracting ids and downloading bytes.

Signed URL only

ts
const url = await w.getAttachmentUrl("123", { readOnly: true })
// Use the URL directly in <img src={url} />, <a download href={url} />, etc.

Blob (preferred for large files)

ts
const { blob, contentType } = await w.fetchAttachmentBlob("123")
const objectUrl = URL.createObjectURL(blob)
// remember to URL.revokeObjectURL(objectUrl) when unmounting

Base64 (for embedding directly)

ts
const { base64, contentType } = await w.fetchAttachmentBase64("123")
const dataUrl = `data:${contentType};base64,${base64}`

Use Blob for anything more than a few KB; Base64 is convenient for inline icons or PDF previews.

Working with cells

Attachment columns hold arrays of file refs. Use the helpers from the package root:

ts
import {
  extractGristAttachmentIdsFromCell,
  extractGristAttachmentId,
  getAttachmentDownloadUrlForCell,
  fetchAttachmentBase64ForCell,
} from "grist-widget-sdk"

const ids = extractGristAttachmentIdsFromCell(row.Photos)
const firstUrl = await getAttachmentDownloadUrlForCell(docApi, row.Photos, 0)

docApi is w.fetchTable-class methods; you'd normally use this helper from a non-React context. Inside React, prefer w.getAttachmentUrl(id) after extracting ids.

Required access level

You need at least requiredAccess: "read table" for read-only tokens, and requiredAccess: "full" for read-write REST. Set this on <GristWidgetProvider> (or via w.configure({ requiredAccess: "full" }) at runtime).

Error handling

Both fetchWithAuth and attachment helpers throw on non-2xx responses with a generic message. For richer handling, inspect the Response yourself:

ts
const response = await w.fetchWithAuth("/api/...", { readOnly: true })
if (!response.ok) {
  const body = await response.text()
  throw new Error(`Grist REST failed (${response.status}): ${body}`)
}

For the 401 retry path, the SDK's internal token cache is cleared automatically — your code keeps working after a brief renewal.

Released under the ISC License.