Skip to content

Write Your First Function

Build a function that stores and retrieves user bookmarks using Database and Cache. By the end, you will have a working API running on the edge with zero external dependencies.

A bookmarks API with three endpoints:

  • POST /bookmarks — save a bookmark (writes to DB)
  • GET /bookmarks — list bookmarks (reads from cache, falls back to DB)
  • DELETE /bookmarks/:id — remove a bookmark (updates DB and invalidates cache)
DatabaseCacheFunctionClientDatabaseCacheFunctionClientalt[Cache hit][Cache miss]GET /bookmarksget("bookmarks")cached dataSELECT * FROM bookmarksrowsput("bookmarks", rows)JSON response
  1. Create the function scaffold

    Terminal window
    mkdir my-bookmarks-api && cd my-bookmarks-api
    npm init -y
    npm install -D typescript @cloudflare/workers-types
  2. Set up TypeScript

    Create tsconfig.json:

    {
    "compilerOptions": {
    "target": "ES2022",
    "module": "ES2022",
    "moduleResolution": "bundler",
    "lib": ["ES2022"],
    "types": ["@cloudflare/workers-types"],
    "strict": true,
    "outDir": "dist",
    "rootDir": "src"
    },
    "include": ["src"]
    }
  3. Configure Aerostack

    Create aerostack.toml:

    name = "my-bookmarks-api"
    main = "src/index.ts"
    compatibility_date = "2024-12-01"
    # These bindings are provided by Aerostack when deployed.
    # For local dev, the CLI creates local instances automatically.
    [[d1_databases]]
    binding = "DB"
    database_name = "aerostack-core"
    database_id = "local"
    [kv_namespaces]
    binding = "CACHE"
  4. Create the database table

    Create schema.sql for local development:

    CREATE TABLE IF NOT EXISTS bookmarks (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    url TEXT NOT NULL,
    title TEXT NOT NULL,
    created_at TEXT DEFAULT (datetime('now'))
    );

    Apply it locally:

    Terminal window
    aerostack db execute --local --file=schema.sql
  5. Write the function

    Create src/index.ts:

    interface Env {
    DB: Database
    CACHE: Cache
    }
    export default {
    async fetch(request: Request, env: Env): Promise<Response> {
    const url = new URL(request.url)
    const path = url.pathname
    // --- POST /bookmarks ---
    if (request.method === 'POST' && path === '/bookmarks') {
    const body = await request.json<{ url: string; title: string }>()
    if (!body.url || !body.title) {
    return Response.json({ error: 'url and title are required' }, { status: 400 })
    }
    const result = await env.DB
    .prepare('INSERT INTO bookmarks (url, title) VALUES (?, ?) RETURNING *')
    .bind(body.url, body.title)
    .first()
    // Invalidate cache so next GET fetches fresh data
    await env.CACHE.delete('bookmarks:all')
    return Response.json(result, { status: 201 })
    }
    // --- GET /bookmarks ---
    if (request.method === 'GET' && path === '/bookmarks') {
    // Try cache first
    const cached = await env.CACHE.get('bookmarks:all', 'json')
    if (cached) {
    return Response.json(cached, {
    headers: { 'X-Cache': 'HIT' }
    })
    }
    // Cache miss — query DB
    const { results } = await env.DB
    .prepare('SELECT * FROM bookmarks ORDER BY created_at DESC')
    .all()
    // Store in cache for 5 minutes
    await env.CACHE.put('bookmarks:all', JSON.stringify(results), {
    expirationTtl: 300
    })
    return Response.json(results, {
    headers: { 'X-Cache': 'MISS' }
    })
    }
    // --- DELETE /bookmarks/:id ---
    if (request.method === 'DELETE' && path.startsWith('/bookmarks/')) {
    const id = path.split('/').pop()
    await env.DB.prepare('DELETE FROM bookmarks WHERE id = ?').bind(id).run()
    await env.CACHE.delete('bookmarks:all')
    return Response.json({ deleted: true })
    }
    return Response.json({ error: 'Not found' }, { status: 404 })
    }
    }
  6. Test locally

    Terminal window
    aerostack dev

    In another terminal:

    Terminal window
    # Create a bookmark
    curl -X POST http://localhost:8787/bookmarks \
    -H "Content-Type: application/json" \
    -d '{"url": "https://aerostack.dev", "title": "Aerostack Docs"}'
    # List bookmarks (first call = cache MISS, second = HIT)
    curl http://localhost:8787/bookmarks -i
    # Delete it
    curl -X DELETE http://localhost:8787/bookmarks/1
  7. Deploy

    Terminal window
    aerostack deploy function

    This uploads your function to your Aerostack project. The platform wires up the real database and cache bindings automatically — no configuration changes needed.

Your function is now running on Cloudflare’s edge in 300+ data centers. It has:

  • A database for persistent storage (env.DB)
  • A cache for fast key-value lookups (env.CACHE)
  • Automatic TLS, DDoS protection, and global distribution
  • Zero cold start overhead

You wrote standard Cloudflare Workers code. Aerostack handled the infrastructure.

This tutorial used two of the six platform bindings. To explore the rest: