# Write Your First Function

> Create, test, and deploy an Aerostack function with database and cache access in under 10 minutes.

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.

## What You Will Build

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)

```mermaid
sequenceDiagram
    participant C as Client
    participant F as Function
    participant CA as Cache
    participant DB as Database

    C->>F: GET /bookmarks
    F->>CA: get("bookmarks")
    alt Cache hit
        CA-->>F: cached data
    else Cache miss
        F->>DB: SELECT * FROM bookmarks
        DB-->>F: rows
        F->>CA: put("bookmarks", rows)
    end
    F-->>C: JSON response
```

## Prerequisites

- [Aerostack CLI installed](/cli) (`npm install -g @aerostack/cli`)
- An Aerostack account with an active project
- Node.js 18+

1. **Create the function scaffold**

   ```bash
   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`:

   ```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`:

   ```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"
   ```

   
     The `database_id` in `aerostack.toml` is only used for local development. When deployed through Aerostack, your function receives the project's actual database binding automatically.
   

4. **Create the database table**

   Create `schema.sql` for local development:

   ```sql
   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:

   ```bash
   aerostack db execute --local --file=schema.sql
   ```

5. **Write the function**

   Create `src/index.ts`:

   ```typescript
   interface Env {
     DB: Database
     CACHE: Cache
   }

   export default {
     async fetch(request: Request, env: Env): Promise {
       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**

   ```bash
   aerostack dev
   ```

   In another terminal:

   ```bash
   # 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**

   ```bash
   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.

## What Just Happened

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.

## Going Further

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

- [**Platform bindings reference**](/functions/platform-access) — code examples for DB, Cache, Queue, AI, Vector Search, and Storage
- [**Patterns and recipes**](/functions/patterns) — production-ready architectures for REST APIs, queue consumers, RAG pipelines, and more
