Skip to content

Quickstart

Your first function, from an empty directory to a call, its logs and a unit test, in about ten minutes.

You will create a config repository with a working sample function, run it locally, call it three ways, write your own, see what it did, and test it. You need Docker (or Podman) and the backd binary; Deno is optional, because the build can run in Deno’s own image.

1. Create a project

backd template project --dir ./shop --realm shop
cd shop

This writes a complete repository: the shop realm with a sample database, a sample function with a test, Dockerfiles, a compose.yaml for local development (MongoDB, backd, the functions executor and the egress proxy) and a CI workflow. Everything you need is in the generated README.md; the parts below follow it.

The function lives here:

config/shop/main/_functions/
  deno.json
  stats/
    function.yaml     how it runs
    index.ts          what it does
    index.test.ts     its unit test

2. Build it and start the stack

backd runs bundles, so build after every change to a function (it refuses to start with stale ones):

export EXECUTOR_TOKEN=$(openssl rand -base64 32)
export CALLBACK_KEY=$(openssl rand -base64 32)
export EGRESS_KEY=$(openssl rand -base64 32)

# With Deno 2.9.7 and backd installed:
CONFIG_DIR=./config backd functions build
# Or in Deno's image, without installing Deno:
docker run --rm -v "$PWD/config:/config" -v "$(command -v backd):/usr/local/bin/backd:ro" \
  -e CONFIG_DIR=/config docker.io/denoland/deno:alpine-2.9.7 backd functions build

docker compose up --build

backd now listens on http://127.0.0.1:8080. The three secrets are shared between backd, the executor and the egress proxy; the Running in production page explains each.

3. Call it

The sample function, stats, is public and read-only, so you can call it without signing in:

curl -X POST http://127.0.0.1:8080/v1/shop/main/_func/stats
# → {"published":0,"mine":0}

Three ways to make the same call:

# HTTP, as above.

# The command line, through the same route any caller uses:
backd functions invoke --function shop/main/stats --url http://127.0.0.1:8080

# The JavaScript client:
#   const stats = await backd.db('main').fn('stats')

4. Write your own

Create a function and edit it:

CONFIG_DIR=./config backd template function --realm shop --database main --name hello

Replace config/shop/main/_functions/hello/index.ts with:

type Context = {
  input: { name?: string } | null;
  error: (status: number, code: string, message: string) => Error;
};

export default async function handler(ctx: Context) {
  const name = ctx.input?.name;
  if (!name) throw ctx.error(400, "name_required", "Send {\"name\": \"…\"}");
  console.log(`greeting ${name}`);
  return { hello: name };
}

Then open function.yaml in the same folder. By default only API keys may call a function; for this tutorial let anyone call it (use invoke: "user != nil" for signed-in users only, see the invoke rules):

invoke: "true"

Rebuild, restart backd (it reads the bundles at startup), and call it:

CONFIG_DIR=./config backd functions build
docker compose up -d --build backd

curl -X POST http://127.0.0.1:8080/v1/shop/main/_func/hello -H 'Content-Type: application/json' -d '{"name": "Ada"}'
# → {"hello":"Ada"}
curl -X POST http://127.0.0.1:8080/v1/shop/main/_func/hello -H 'Content-Type: application/json' -d '{}'
# → 400 {"error": {"code": "name_required", "message": "Send {\"name\": \"…\"}", ...}}

While you edit function code, run backd locally with BACKD_DEV=true instead: it rebuilds function sources and reloads the config as you save, with no restart (Dev mode).

5. See what happened

Create the first administrator, sign in, and read the function’s history and logs:

docker compose exec backd /backd bootstrap --realm shop --email ops@example.com
backd login --realm shop --url http://127.0.0.1:8080 --email ops@example.com

backd functions history --function shop/main/hello
# TIME                      STATUS  CODE  DURATION  ACTOR      REQUEST-ID
# 2026-09-30T09:12:41.002Z  ok      -     3ms       anonymous  darf9mq5mh5g00aq9df0

backd functions logs --function shop/main/hello
# 2026-09-30T09:12:41.002Z darf9mq5mh5g00aq9df0 [log] greeting Ada

History never contains a call’s input or output; only what happened. See Function logs.

6. Test it without any of this

deno test runs a function’s own logic against a fake ctx: no MongoDB, no backd, no network.

deno test config/shop/main/_functions/

The sample stats/index.test.ts shows the pattern. See Build, dev and test.

Where next

  • Read data and change it with the caller’s permissions: Writing a function.
  • Do slow work in the background, and know when it’s done: Async jobs.
  • Run something every night: Scheduled functions. Note that async jobs and cron need a worker: in the generated compose.yaml, backd already runs one (serve --with-worker).
  • Complete recipes: the Cookbook.
Edit this page · Last updated 19 Oct 2026