Ada Extensions
Guides

Network and secrets

Call APIs with fetch, keep API keys in the Keychain, handle errors.

fetch

Extensions have a fetch much like the browser's. It only goes to https:// URLs on the domains you ask for as network:<domain> in manifest.json.

manifest.json
"permissions": ["network:api.open-meteo.com"]
const url = new URL("https://api.open-meteo.com/v1/forecast");
url.searchParams.set("latitude", "41.01");
url.searchParams.set("longitude", "28.97");
url.searchParams.set("current", "temperature_2m");

const res = await fetch(url, { timeout: 10_000 });
if (!res.ok) throw new Error(`Open-Meteo: ${res.status}`);
const data = await res.json<{ current: { temperature_2m: number } }>();

Shortcut: ctx.network.fetchJSON<T>(url, init) makes the request and returns .json().

Request body

await fetch("https://api.example.com/items", {
  method: "POST",
  headers: { authorization: `Bearer ${token}` },
  body: { title: "New" },                       // object → JSON, content-type set for you
});

await fetch("https://api.example.com/login", {
  method: "POST",
  body: new URLSearchParams({ user: "ada" }),   // form
});

A string is sent as is, URLSearchParams as a form, any other value as JSON. Bodies are 1 MB at most.

Errors

ErrorWhen
PermissionErrorThe domain isn't in your network: permissions, an http:// URL, or a redirect to a domain that isn't
NetworkErrorNo connection, DNS failure, refused connection, a response over 5 MB, too many requests
TimeoutErrortimeout (15 s by default) passed

4xx/5xx responses don't throw; check res.ok and res.status.

async update(ctx) {
  try {
    const data = await ctx.network.fetchJSON<Weather>(url);
    ctx.storage.set("last", data);              // keep the last good answer
    return view(data);
  } catch (error) {
    const last = ctx.storage.get<Weather>("last");
    if (error instanceof NetworkError && last) return view(last, { stale: true });
    throw error;
  }
}

To skip requests while offline, use await AdanotchKit.system.isOnline() and the network.change event.

Secrets

Don't put API keys or passwords in your code or in storage. Declare a secret setting in manifest.json; the user types it in Settings, and Adanotch keeps it in the macOS Keychain.

manifest.json
"settings": [
  { "key": "token", "type": "secret", "label": "GitHub token" }
]
async update() {
  const token = await AdanotchKit.secrets.get("token");
  if (!token) {
    return { expanded: ui.text("Enter a GitHub token in Settings.", { tone: "secondary" }) };
  }
  const res = await fetch("https://api.github.com/user", {
    headers: { authorization: `Bearer ${token}`, accept: "application/vnd.github+json" },
  });
  // …
}
  • secret settings don't appear in ctx.settings and can't have a default.
  • secrets.get returns null until the user sets a value. When the user saves or deletes it, update runs again.
  • Secrets belong to the extension's ID; no other extension can read them.

Don't print a secret with console.log: logs show in Settings › Developer and in the adanotch dev output.

Images

ui.image(src) follows the same list: to show avatars, ask for the image domain too.

"permissions": ["network:api.github.com", "network:avatars.githubusercontent.com"]

On this page