Skip to content

Examples & Tips

Complete, copy-pasteable functions that show the pieces working together, followed by practical tips for writing reliable Cloud Code.


Example: an HTTP endpoint backed by your database

A POST /api/fn/create-favorite endpoint that saves a favorite for the calling user and returns the new count.

index.js
export async function handler(ctx) {
  // Only accept POST
  if (ctx.req.method !== "POST") {
    return { status: 405, body: { error: "method_not_allowed" } };
  }

  const { note } = ctx.req.body ?? {};
  if (!note) {
    return { status: 400, body: { error: "note is required" } };
  }

  // Tie the row to the authenticated caller
  const userId = ctx.consumer?.id ?? null;

  await ctx.appambit.db(
    "INSERT INTO favorites (user_id, note, created_at) VALUES (?, ?, ?)",
    [userId, note, new Date().toISOString()]
  );

  const res = await ctx.appambit.db(
    "SELECT count(*) FROM favorites WHERE user_id = ?",
    [userId]
  );
  const count = res.results[0].rows[0][0];

  return { status: 201, body: { ok: true, count } };
}

Example: aggregate the database and publish to the CMS

A function that counts yesterday's orders and publishes a summary entry — a good fit for a manual run or a daily job.

index.js
export async function handler(ctx) {
  const res = await ctx.appambit.db(
    "SELECT count(*), coalesce(sum(total), 0) FROM orders WHERE created_at >= date('now', '-1 day')"
  );
  const [orderCount, revenue] = res.results[0].rows[0];

  const entry = await ctx.appambit.cms.publish("daily_reports", {
    title: `Daily report`,
    orders: orderCount,
    revenue,
  });

  ctx.log(`published report ${entry.data.id}: ${orderCount} orders`);
  return { orders: orderCount, revenue };
}

Example: react to a data change with a push notification

An event function wired to insert on the orders table. When a new order lands, it sends a push.

index.js
export async function handler(ctx) {
  const change = ctx.event ?? {};
  ctx.log("new order event", change);

  await ctx.appambit.push({
    title: "New order!",
    body: "You just received a new order.",
  });

  return { notified: true };
}

More on push notifications

ctx.appambit.push sends through the same delivery pipeline as the dashboard and the API. For payload options, audience targeting, and platform (FCM/APNs) requirements, see the Push Notifications guide and the Push Notifications SDK reference.


Example: call a third-party API with a secret

Read an API key from secrets, call an external service, and return the result.

index.js
export async function handler(ctx) {
  const apiKey = ctx.secrets.WEATHER_API_KEY;
  const city = ctx.req?.body?.city ?? "London";

  const resp = await fetch(
    `https://api.example.com/weather?city=${encodeURIComponent(city)}`,
    { headers: { Authorization: `Bearer ${apiKey}` } }
  );

  if (!resp.ok) {
    ctx.log(`weather API returned ${resp.status}`);
    return { status: 502, body: { error: "upstream_failed" } };
  }

  const weather = await resp.json();
  return { body: { city, weather } };
}

Tips

Always parameterize your SQL

Pass values through the params array with ? placeholders — never build SQL by concatenating strings. It's safer and it's the only way to handle quoting correctly.

// Good
await ctx.appambit.db("SELECT * FROM users WHERE email = ?", [email]);
// Risky — don't do this
await ctx.appambit.db(`SELECT * FROM users WHERE email = '${email}'`);

Read the input defensively

ctx.req, its body, and its query may be missing or partial. Default everything and validate before you use it:

const { name = "world", count = 1 } = ctx.req?.body ?? {};

Enforce per-user access yourself

ctx.consumer.id tells you who called an HTTP function, but the function still runs with your app's full scope. If a user should only see their own data, filter by ctx.consumer.id in your query — the platform won't do it for you.

Log the shape, not the secrets

ctx.log is your window into a run. Log inputs, decisions, and row counts — but never log secret values or full tokens.

Return early and keep runs short

Validate first and bail out fast on bad input. Request only the columns and rows you actually use. Shorter runs mean lower compute cost and snappier HTTP responses.

Batch related writes

When you have several writes that must all succeed or all fail, use ctx.appambit.batch(statements, true) to run them in a transaction rather than issuing separate db calls.

Deploy small, activate deliberately

Deploying builds a new version but does not make it live — you activate it explicitly. That gap is your safety net: test the new version, and if anything's off, activate the previous one to roll back instantly.


See also

  • Writing Functions


    The full ctx reference and data SDK.

    View Guide ➔

  • Triggers


    Manual, HTTP, and event triggers in detail.

    View Guide ➔

  • Configuration & Limits


    Env vars, secrets, quotas, billing, and error codes.

    View Guide ➔