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.
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.
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.
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.
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
ctxreference and data SDK. -
Triggers
Manual, HTTP, and event triggers in detail.
-
Configuration & Limits
Env vars, secrets, quotas, billing, and error codes.