Edge Functions
Your own server code, next to your database: receive webhooks, take payments, send emails or call other APIs, without running a server.
How it works
- A function is a TypeScript module that exports a handler: it receives a standard
Requestand returns aResponse. It runs on Deno, sofetch, Web Crypto andnpm:packages work. - Each project's functions run in their own sandbox (gVisor). A function can use the network and its project's secrets, and nothing else: no files, no other programs, no database passwords.
- Functions start on the first call and sleep after 10 minutes without calls. Every deploy is a new version.
1. Write and deploy
// functions/hello.ts
export default async (req: Request): Promise<Response> => {
const { name } = await req.json().catch(() => ({ name: "world" }));
console.log("greeting", name);
return Response.json({ message: `Hello ${name}!` });
};In the dashboard, open Edge Functions → New function, pick a name (it becomes part of the URL) and click Deploy. Builders (developers, admins, owners) can deploy; deleting a function needs an admin or owner.
2. Call it
# with the project's publishable key
curl -X POST "$ESPOBASE_URL/functions/v1/hello" \
-H "apikey: $ESPOBASE_PUBLISHABLE_KEY" \
-H "content-type: application/json" \
-d '{"name":"Ada"}'
// from your app (@espobase/client): sends the signed-in user's token too
const { data, error } = await espobase.functions.invoke("hello", { body: { name: "Ada" } });By default a function needs the project's API key, like the REST API. Set Who can call it to Anyone with the URL for webhooks (from Stripe, GitHub, …) and check their signatures inside the function.
3. Know who is calling
export default (req: Request) => {
if (req.headers.get("x-espobase-auth") !== "authenticated") {
return new Response("Sign in first", { status: 401 });
}
return Response.json({ hello: req.headers.get("x-espobase-user-email") });
};When a signed-in user calls (espobase.functions.invoke sends their token), Espobase verifies the token and passes the user in x-espobase-user-id, x-espobase-user-email and x-espobase-user-role. x-espobase-auth is authenticated, anonymous, service or public. Clients can't set these headers themselves.
4. Secrets
const stripeKey = Deno.env.get("STRIPE_SECRET_KEY");
const projectUrl = Deno.env.get("ESPOBASE_URL");Add keys like STRIPE_SECRET_KEY under Edge Functions → Secrets and read them with Deno.env.get("STRIPE_SECRET_KEY"). Values are stored encrypted and can't be viewed again; running functions restart when a secret changes. ESPOBASE_URL (your project's API URL) is always set.
Logs
What a function prints with console.log and the errors it throws appear under the function in the dashboard, call by call, with the status and duration. Logs are kept as long as your plan keeps request logs.
Deploy from the CLI
npx espobase login npx espobase link <project-ref> npx espobase functions deploy hello # functions/hello.ts npx espobase functions logs hello
deploy creates the function on its first deploy (add --public for webhooks). The code comes from functions/<name>.ts, functions/<name>/index.ts or --file. In CI, use a personal access token with the Deploy edge functions permission.
Security
- Each project's functions run apart from other projects and from Espobase's databases, under gVisor, with a read-only file system and no special privileges.
- API keys, cookies and Espobase's internal headers never reach your code; responses can't set cookies on the API's address.
- Public functions get no key check: verify webhook signatures in your code.
- Errors thrown by a function reach callers only as a generic error; the details go to the function's logs.
Limits
- Free: 500K calls a month. When they're used up, calls are refused until the next billing cycle.
- Pro: 2M calls a month.
- Group: 10M calls a month.
- Every call: up to 10 seconds and 6 MB of request or response body. Up to 25 functions per project, 256 KB of code each, and 50 secrets.