Skip to content

Serverless Functions

Deploy custom backend logic as serverless functions. Functions are written in TypeScript or JavaScript and run in an isolated Deno environment with database access, environment variables, and network capabilities.

POST /v1/{app_id}/functions
Authorization: Bearer {token}
{
"name": "hello-world",
"code": "export default async function handler(req) {\n return new Response(JSON.stringify({ message: 'Hello!' }), {\n headers: { 'Content-Type': 'application/json' }\n });\n}",
"description": "A simple greeting function",
"trigger": {
"type": "http",
"config": {}
}
}

Required fields:

  • name — Unique name (1-100 characters)
  • code — Function source code with a default export handler

Optional fields:

  • description — What the function does
  • envVars — Key-value pairs for environment variables (encrypted at rest)
  • timeoutMs — Max execution time (default: 30000, max: 300000)
  • memoryLimitMb — Memory limit (default: 128, range: 64-1024)
  • trigger — How the function is invoked
TypeDescriptionConfig
httpCalled via HTTP requests (default){}
cronRuns on a schedule{"schedule": "*/5 * * * *"}
websocketFires on custom WebSocket events{"event": "event_name"}

Functions receive a Request and must return a Response:

export default async function handler(req: Request, ctx: any): Promise<Response> {
const body = await req.json();
return new Response(JSON.stringify({ result: 'ok' }), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
}

Available inside a function:

  • Standard Web APIs (fetch, Request, Response, Headers, URL, etc.)
  • Environment variables via ctx.env.VAR_NAME
  • Database access via ctx.db.query(sql)
  • User info via ctx.user (when invoked with end-user JWT)
  • Network access

Functions respect RLS policies based on how they’re invoked:

InvocationRoleRLS
End-user JWTbutterbase_userEnforced — sees only user’s data
Platform API keybutterbase_serviceBypassed — sees all data
Cron triggerbutterbase_serviceBypassed — sees all data
export default async function handler(req: Request, ctx: any): Promise<Response> {
if (!ctx.user) {
return new Response('Unauthorized', { status: 401 });
}
// Automatically filtered to current user's orders (RLS enforced)
const orders = await ctx.db.query('SELECT * FROM orders');
return new Response(JSON.stringify(orders.rows), {
headers: { 'Content-Type': 'application/json' }
});
}

Use ctx.db.asUser() and ctx.db.asAnon() to run queries under a specific role:

export default async function handler(req: Request, ctx: any): Promise<Response> {
const userId = 'some-user-uuid';
// Runs as butterbase_user with RLS enforced
const userPosts = await ctx.db.asUser(userId, async (db) => {
const result = await db.query('SELECT * FROM posts');
return result.rows;
});
// Runs as butterbase_anon with RLS enforced
const publicProducts = await ctx.db.asAnon(async (db) => {
const result = await db.query('SELECT * FROM products');
return result.rows;
});
return new Response(JSON.stringify({ userPosts, publicProducts }), {
headers: { 'Content-Type': 'application/json' }
});
}
// Deploy with: envVars: { "API_KEY": "secret123", "BASE_URL": "https://api.example.com" }
export default async function handler(req: Request, ctx: any): Promise<Response> {
const apiKey = ctx.env.API_KEY;
const baseUrl = ctx.env.BASE_URL;
const response = await fetch(`${baseUrl}/data`, {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
return new Response(await response.text());
}

HTTP-triggered functions are available at:

ANY /v1/{app_id}/fn/{function_name}

Any HTTP method is supported. End-user tokens are forwarded to the function.

const response = await fetch(`${API_BASE}/v1/${appId}/fn/hello-world`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${userAccessToken}`
},
body: JSON.stringify({ input: 'data' })
});

Use standard cron expressions:

ExpressionSchedule
* * * * *Every minute
*/5 * * * *Every 5 minutes
0 * * * *Every hour
0 9 * * *Daily at 9 AM
0 0 * * 1Every Monday at midnight

Each function tracks: total invocation count, error count and rate, average execution duration, and last invocation time.

Logs include: HTTP method and path, status code, execution duration, memory usage, and error messages with stack traces.