# Platform Bindings

> Deep dive into the six native bindings available to every Aerostack function — Database, Cache, Queue, AI, Vector Search, and Storage.

Every Aerostack function receives six native bindings through its `env` parameter. These are not HTTP clients — they are in-process Cloudflare bindings with near-zero latency.

```typescript
interface Env {
  DB: Database           // Database (SQL, SQLite-compatible)
  CACHE: Cache       // Cache (key-value with TTL)
  QUEUE: Queue             // Queue (background jobs)
  AI: AI                   // AI — Multi-provider LLM
  VECTORIZE: VectorSearch // Vector Search (semantic similarity)
  STORAGE: Storage        // Storage (object storage with CDN)
}
```

---

## Database (`env.DB`)

Aerostack's edge database is SQL-based (SQLite-compatible). Full SQL support, automatic replication, and zero-latency access from your function.

### Query patterns

```typescript
// Single row
const user = await env.DB
  .prepare('SELECT * FROM users WHERE id = ?')
  .bind(userId)
  .first()

// Multiple rows
const { results } = await env.DB
  .prepare('SELECT * FROM users WHERE active = ? ORDER BY created_at DESC LIMIT ?')
  .bind(1, 50)
  .all()

// Insert with returning
const created = await env.DB
  .prepare('INSERT INTO users (email, name) VALUES (?, ?) RETURNING *')
  .bind(email, name)
  .first()

// Update
const { meta } = await env.DB
  .prepare('UPDATE users SET name = ? WHERE id = ?')
  .bind(newName, userId)
  .run()
console.log(`Rows changed: ${meta.changes}`)

// Delete
await env.DB.prepare('DELETE FROM sessions WHERE expires_at < ?').bind(Date.now()).run()
```

### Batch operations

Execute multiple statements in a single round trip. All statements run in an implicit transaction — if any fails, they all roll back.

```typescript
const results = await env.DB.batch([
  env.DB.prepare('INSERT INTO orders (user_id, total) VALUES (?, ?)').bind(userId, total),
  env.DB.prepare('UPDATE users SET order_count = order_count + 1 WHERE id = ?').bind(userId),
  env.DB.prepare('INSERT INTO audit_log (action, user_id) VALUES (?, ?)').bind('order_created', userId),
])
```

### Typed results

```typescript
interface User {
  id: number
  email: string
  name: string
  created_at: string
}

const user = await env.DB
  .prepare('SELECT * FROM users WHERE id = ?')
  .bind(userId)
  .first()
// user is User | null
```

  The database uses SQLite syntax. Use `datetime('now')` for timestamps, `IFNULL` instead of `COALESCE` for simple defaults, and `INTEGER PRIMARY KEY AUTOINCREMENT` for auto-incrementing IDs.

**When to use:** Structured data, relational queries, transactions, anything that needs ACID guarantees.

---

## Cache (`env.CACHE`)

Aerostack's cache provides a globally distributed key-value store. Reads are fast everywhere (eventually consistent). Writes propagate globally within 60 seconds.

### Basic operations

```typescript
// String values
await env.CACHE.put('user:123:profile', JSON.stringify(profile))
const raw = await env.CACHE.get('user:123:profile')
const profile = raw ? JSON.parse(raw) : null

// Typed get — parses JSON automatically
const profile = await env.CACHE.get('user:123:profile', 'json')

// Delete
await env.CACHE.delete('user:123:profile')

// Check existence without reading the value
const meta = await env.CACHE.getWithMetadata('user:123:profile')
if (meta.value === null) {
  // Key does not exist
}
```

### TTL (time-to-live)

```typescript
// Expire after 1 hour (3600 seconds)
await env.CACHE.put('session:abc', tokenData, { expirationTtl: 3600 })

// Expire at a specific timestamp (Unix seconds)
const oneHourFromNow = Math.floor(Date.now() / 1000) + 3600
await env.CACHE.put('session:abc', tokenData, { expiration: oneHourFromNow })
```

### Metadata

Attach metadata to a key without increasing the value size:

```typescript
await env.CACHE.put('report:daily', reportJson, {
  expirationTtl: 86400,
  metadata: { generatedAt: Date.now(), version: 3 }
})

const { value, metadata } = await env.CACHE.getWithMetadata('report:daily', 'json')
console.log(`Report version: ${metadata.version}`)
```

### List keys

```typescript
// List all keys with a prefix
const { keys } = await env.CACHE.list({ prefix: 'user:123:' })
for (const key of keys) {
  console.log(key.name, key.expiration)
}
```

### Cache-aside pattern

The most common caching strategy — check cache first, query DB on miss, populate cache:

```typescript
async function getCachedUser(env: Env, userId: number) {
  const cacheKey = `user:${userId}`

  // 1. Try cache
  const cached = await env.CACHE.get(cacheKey, 'json')
  if (cached) return cached

  // 2. Query DB
  const user = await env.DB
    .prepare('SELECT * FROM users WHERE id = ?')
    .bind(userId)
    .first()

  if (!user) return null

  // 3. Populate cache (5 min TTL)
  await env.CACHE.put(cacheKey, JSON.stringify(user), { expirationTtl: 300 })

  return user
}
```

**When to use:** Session tokens, computed results, rate limit counters, configuration, any read-heavy data that can tolerate eventual consistency.

---

## Queue (`env.QUEUE`)

Aerostack's queue provides reliable, at-least-once message delivery. Send messages from your `fetch()` handler and process them asynchronously in a `queue()` handler.

### Sending messages

```typescript

  async fetch(request: Request, env: Env): Promise {
    // Send a single message
    await env.QUEUE.send({
      type: 'send-email',
      to: 'user@example.com',
      subject: 'Welcome',
      body: 'Thanks for signing up.'
    })

    // Send a batch of messages
    await env.QUEUE.sendBatch([
      { body: { type: 'resize-image', key: 'uploads/photo1.jpg' } },
      { body: { type: 'resize-image', key: 'uploads/photo2.jpg' } },
      { body: { type: 'resize-image', key: 'uploads/photo3.jpg' } },
    ])

    return Response.json({ queued: true })
  }
}
```

### Processing messages

Add a `queue()` handler to your function. Cloudflare delivers messages in batches:

```typescript

  async fetch(request: Request, env: Env): Promise {
    // ... your HTTP handler
  },

  async queue(batch: MessageBatch, env: Env): Promise<void> {
    for (const msg of batch.messages) {
      try {
        const job = msg.body as { type: string; [key: string]: unknown }

        switch (job.type) {
          case 'send-email':
            await sendEmail(job.to as string, job.subject as string, job.body as string)
            break
          case 'resize-image':
            await resizeImage(env, job.key as string)
            break
          default:
            console.error(`Unknown job type: ${job.type}`)
        }

        msg.ack() // Remove from queue
      } catch (err) {
        msg.retry() // Re-queue for retry
      }
    }
  }
}
```

### Delayed messages

```typescript
// Delay delivery by 60 seconds
await env.QUEUE.send(
  { type: 'reminder', userId: 123 },
  { delaySeconds: 60 }
)
```

  Messages are delivered **at least once**. Your `queue()` handler must be idempotent — processing the same message twice should produce the same result. Use a unique job ID and check for duplicates if needed.

**When to use:** Email sending, image processing, webhook fan-out, any work that should not block the HTTP response.

---

## AI (`env.AI`)

Run LLM inference directly from your function. Supports multiple providers and models with a unified interface.

### Text generation

```typescript
// Basic completion
const response = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
  messages: [
    { role: 'system', content: 'You are a helpful assistant.' },
    { role: 'user', content: 'Explain edge computing in one paragraph.' }
  ]
})
console.log(response.response)

// With parameters
const response = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
  messages: [{ role: 'user', content: 'Write a haiku about databases.' }],
  max_tokens: 100,
  temperature: 0.7
})
```

### Streaming responses

```typescript
const stream = await env.AI.run('@cf/meta/llama-3.1-8b-instruct', {
  messages: [{ role: 'user', content: 'Tell me a story.' }],
  stream: true
})

return new Response(stream, {
  headers: { 'Content-Type': 'text/event-stream' }
})
```

### Text embeddings

Generate vector embeddings for semantic search:

```typescript
const embedding = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
  text: 'How do I deploy a function?'
})
// embedding.data[0] is a float array (768 dimensions for bge-base)
```

### Image classification

```typescript
const imageBuffer = await env.STORAGE.get('photos/cat.jpg')
const result = await env.AI.run('@cf/microsoft/resnet-50', {
  image: [...new Uint8Array(await imageBuffer.arrayBuffer())]
})
// result: [{ label: 'tabby cat', score: 0.95 }, ...]
```

### Text classification and summarization

```typescript
// Summarize
const summary = await env.AI.run('@cf/facebook/bart-large-cnn', {
  input_text: longArticle,
  max_length: 200
})

// Classify sentiment
const sentiment = await env.AI.run('@cf/huggingface/distilbert-sst-2-int8', {
  text: 'This product is amazing!'
})
```

  See the full list of available models in the [Cloudflare Workers AI documentation](https://developers.cloudflare.com/workers-ai/models/). All models accessible via the Cloudflare AI binding are available in your Aerostack functions.

**When to use:** Text generation, summarization, classification, embeddings, image analysis — any ML inference that should run close to your data.

---

## Vector Search (`env.VECTORIZE`)

Aerostack's vector search provides native vector similarity search. Store embeddings and query them with sub-millisecond latency from your function.

### Insert vectors

```typescript
// Generate embedding, then store it
const embedding = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
  text: 'Aerostack functions run on Cloudflare Workers'
})

await env.VECTORIZE.upsert([{
  id: 'doc-123',
  values: embedding.data[0],
  metadata: {
    title: 'Functions Overview',
    category: 'docs',
    source: 'knowledge-base'
  }
}])
```

### Query by similarity

```typescript
// Embed the query
const queryEmbedding = await env.AI.run('@cf/baai/bge-base-en-v1.5', {
  text: 'How do I deploy edge functions?'
})

// Find the 5 most similar documents
const results = await env.VECTORIZE.query(queryEmbedding.data[0], {
  topK: 5,
  returnMetadata: 'all'
})

for (const match of results.matches) {
  console.log(`${match.id} — score: ${match.score}, title: ${match.metadata?.title}`)
}
```

### Filter by metadata

```typescript
const results = await env.VECTORIZE.query(queryEmbedding.data[0], {
  topK: 10,
  returnMetadata: 'all',
  filter: {
    category: 'docs'
  }
})
```

### Delete vectors

```typescript
await env.VECTORIZE.deleteByIds(['doc-123', 'doc-456'])
```

**When to use:** Semantic search, RAG (retrieval-augmented generation), recommendation engines, duplicate detection, any similarity-based lookup.

---

## Storage (`env.STORAGE`)

Aerostack Storage provides S3-compatible object storage with zero egress fees. Store files, images, PDFs, or any binary data.

### Upload objects

```typescript
// Store a file from a request
const formData = await request.formData()
const file = formData.get('file') as File

await env.STORAGE.put(`uploads/${file.name}`, file.stream(), {
  httpMetadata: { contentType: file.type }
})

// Store generated content
const report = JSON.stringify(analyticsData)
await env.STORAGE.put('reports/daily.json', report, {
  httpMetadata: { contentType: 'application/json' }
})
```

### Download objects

```typescript
const object = await env.STORAGE.get('uploads/photo.jpg')

if (!object) {
  return new Response('Not found', { status: 404 })
}

return new Response(object.body, {
  headers: {
    'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
    'Content-Length': String(object.size)
  }
})
```

### Delete objects

```typescript
// Single delete
await env.STORAGE.delete('uploads/old-file.pdf')

// Batch delete
await env.STORAGE.delete(['temp/a.txt', 'temp/b.txt', 'temp/c.txt'])
```

### List objects

```typescript
const listed = await env.STORAGE.list({ prefix: 'uploads/', limit: 100 })

for (const object of listed.objects) {
  console.log(`${object.key} — ${object.size} bytes, uploaded ${object.uploaded}`)
}

// Pagination
if (listed.truncated) {
  const nextPage = await env.STORAGE.list({
    prefix: 'uploads/',
    cursor: listed.cursor
  })
}
```

### Presigned URLs and multipart uploads

```typescript
// Create a multipart upload for large files
const upload = await env.STORAGE.createMultipartUpload('videos/large.mp4', {
  httpMetadata: { contentType: 'video/mp4' }
})

// Upload parts
const part1 = await upload.uploadPart(1, chunk1)
const part2 = await upload.uploadPart(2, chunk2)

// Complete
await upload.complete([part1, part2])
```

  Aerostack Storage has **zero egress fees**. You pay only for storage and operations, never for bandwidth. This makes it ideal for serving files, images, and media directly from your function.

**When to use:** File uploads, image storage, document processing, backups, serving static assets, any binary data.

---

## Quick Reference

| Binding | Type | Access | Best For |
|---|---|---|---|
| `env.DB` | Database | `env.DB.prepare().bind().all()` | Structured data, transactions |
| `env.CACHE` | Cache | `env.CACHE.get()` / `.put()` | Hot data, sessions, counters |
| `env.QUEUE` | Queue | `env.QUEUE.send()` | Background jobs, async work |
| `env.AI` | Ai | `env.AI.run(model, input)` | LLM, embeddings, classification |
| `env.VECTORIZE` | VectorSearch | `env.VECTORIZE.query()` | Semantic search, RAG |
| `env.STORAGE` | Storage | `env.STORAGE.put()` / `.get()` | Files, images, binary data |
