SDK (Beta)Cache & Queue

Cache & Queue

Two simpler but essential modules for production applications. Cache gives you sub-millisecond key-value storage at the edge. Queue lets you offload work to background jobs with automatic retries.

⚠️

Beta — Cache and Queue APIs are stable but may receive non-breaking additions.


Cache

Globally replicated key-value cache with TTL support, atomic counters, and bulk operations. Data is replicated globally for low-latency reads from any edge location.

What you can build with Cache

  • Session storage — Store user sessions with automatic expiry
  • Rate limiting — Atomic counters to track request rates per user/IP
  • Feature flags — Store and update feature flags without redeploying
  • Computed results — Cache expensive database queries or API responses
  • View counters — Atomic increment for page views, likes, votes
  • Shopping carts — Temporary cart data with TTL expiry

Basic operations

// Set a value (with optional TTL in seconds)
await sdk.cache.set('user:123', { name: 'Jane', plan: 'pro' }, { ttl: 3600 })
 
// Set with metadata
await sdk.cache.set('session:abc', sessionData, {
  ttl: 1800,
  metadata: { userId: '123', device: 'mobile' },
})
 
// Get a value (null if not found or expired)
const user = await sdk.cache.get('user:123')
 
// Check existence
const exists = await sdk.cache.has('user:123')
 
// Delete
await sdk.cache.delete('user:123')

Bulk operations

Efficiently get, set, or delete multiple keys in a single round-trip:

// Get many
const values = await sdk.cache.getMany(['user:1', 'user:2', 'user:3'])
// { 'user:1': {...}, 'user:2': {...}, 'user:3': null }
 
// Set many
await sdk.cache.setMany({
  'user:1': { name: 'Alice' },
  'user:2': { name: 'Bob' },
  'user:3': { name: 'Carol' },
}, { ttl: 3600 })
 
// Delete many
await sdk.cache.deleteMany(['user:1', 'user:2', 'user:3'])

List keys

// List all keys with a prefix
const keys = await sdk.cache.keys({ prefix: 'session:' })
// ['session:abc', 'session:def', 'session:ghi']
 
// List with metadata
const entries = await sdk.cache.list({ prefix: 'session:' })
// [{ key: 'session:abc', metadata: { userId: '123' } }, ...]

Atomic counters

Increment or decrement values atomically. Perfect for rate limiting, view counts, and voting:

// Increment (creates key with value 1 if it doesn't exist)
const newCount = await sdk.cache.increment('views:post-123')
// 1, 2, 3, ...
 
// Increment by a specific amount
const newCount = await sdk.cache.increment('views:post-123', 5)
 
// Use for rate limiting
async function checkRateLimit(userId: string, limit: number) {
  const key = `ratelimit:${userId}:${Math.floor(Date.now() / 60000)}`
  const count = await sdk.cache.increment(key)
 
  if (count === 1) {
    // First request in this minute window -- set TTL
    await sdk.cache.expire(key, 60)
  }
 
  return count <= limit
}

Set expiry on existing key

await sdk.cache.expire('session:abc', 1800) // 30 minutes from now

Flush all cache

await sdk.cache.flush() // Delete everything -- use with caution

Complete example: session store

session-store.ts
import { AerostackClient } from '@aerostack/sdk'
 
const { sdk } = new AerostackClient({ projectId, apiKey })
 
async function createSession(userId: string, device: string) {
  const sessionId = crypto.randomUUID()
 
  await sdk.cache.set(`session:${sessionId}`, {
    userId,
    device,
    createdAt: Date.now(),
    lastActive: Date.now(),
  }, {
    ttl: 86400, // 24 hours
    metadata: { userId, device },
  })
 
  return sessionId
}
 
async function getSession(sessionId: string) {
  const session = await sdk.cache.get(`session:${sessionId}`)
  if (!session) return null
 
  // Refresh TTL on access
  await sdk.cache.set(`session:${sessionId}`, {
    ...session,
    lastActive: Date.now(),
  }, { ttl: 86400 })
 
  return session
}
 
async function destroySession(sessionId: string) {
  await sdk.cache.delete(`session:${sessionId}`)
}
 
async function getUserSessions(userId: string) {
  const entries = await sdk.cache.list({ prefix: 'session:' })
  return entries.filter(e => e.metadata?.userId === userId)
}

Complete example: feature flags

feature-flags.ts
async function setFlag(name: string, value: boolean, metadata?: Record<string, any>) {
  await sdk.cache.set(`flag:${name}`, { enabled: value, ...metadata })
}
 
async function getFlag(name: string): Promise<boolean> {
  const flag = await sdk.cache.get(`flag:${name}`)
  return flag?.enabled ?? false
}
 
async function getAllFlags() {
  const keys = await sdk.cache.keys({ prefix: 'flag:' })
  const values = await sdk.cache.getMany(keys)
  return Object.fromEntries(
    keys.map(k => [k.replace('flag:', ''), values[k]?.enabled ?? false])
  )
}
 
// Usage
await setFlag('new-checkout', true)
await setFlag('dark-mode', true, { rollout: 50 })
 
if (await getFlag('new-checkout')) {
  // Show new checkout flow
}

Cache API reference

MethodSignatureReturns
sdk.cache.get(key: string) => Promise<T | null>Cached value or null
sdk.cache.set(key: string, value: any, opts?: { ttl?, metadata? }) => Promise<void>Nothing
sdk.cache.delete(key: string) => Promise<void>Nothing
sdk.cache.has(key: string) => Promise<boolean>Boolean
sdk.cache.getMany(keys: string[]) => Promise<Record<string, T>>Map of key to value
sdk.cache.setMany(entries: Record<string, any>, opts?) => Promise<void>Nothing
sdk.cache.deleteMany(keys: string[]) => Promise<void>Nothing
sdk.cache.keys(opts?: { prefix? }) => Promise<string[]>Array of keys
sdk.cache.list(opts?: { prefix? }) => Promise<{ key, metadata }[]>Keys with metadata
sdk.cache.increment(key: string, amount?: number) => Promise<number>New value
sdk.cache.expire(key: string, ttl: number) => Promise<void>Nothing
sdk.cache.flush() => Promise<void>Nothing

Queue

Background job processing with configurable retries, delays, and status tracking. Jobs are durably stored and guaranteed to execute at least once.

What you can build with Queue

  • Email notifications — Send welcome emails, password resets, order confirmations
  • PDF generation — Generate invoices, reports, or certificates in the background
  • Batch imports — Process CSV uploads row by row without blocking the request
  • Image processing — Resize, crop, or optimize uploaded images
  • Webhook delivery — Retry failed webhook deliveries with exponential backoff
  • Scheduled tasks — Delay job execution for reminders, follow-ups, or scheduled reports
  • Data sync — Sync data between Aerostack and external systems

Enqueue a job

// Simple job
await sdk.queue.enqueue('send-welcome-email', {
  userId: 'user-123',
  email: '[email protected]',
  name: 'Jane',
})
 
// Job with options
await sdk.queue.enqueue('generate-report', {
  reportType: 'monthly',
  month: '2026-03',
  userId: 'user-123',
}, {
  delaySeconds: 60,  // Wait 1 minute before processing
  maxRetries: 5,     // Retry up to 5 times on failure
})

Check job status

const job = await sdk.queue.getJob(jobId)
// { id, type, status, payload, attempts, maxRetries, createdAt, scheduledAt, completedAt }
StatusDescription
pendingWaiting to be processed (or delayed)
processingCurrently being executed
completedSuccessfully finished
failedAll retries exhausted
cancelledManually cancelled

List jobs

const jobs = await sdk.queue.listJobs({
  type: 'send-welcome-email',   // optional filter
  status: 'pending',             // optional filter
  limit: 50,
})

Cancel a job

await sdk.queue.cancelJob(jobId)

You can only cancel jobs in pending status. Jobs that are already processing will complete their current attempt.


Complete example: email notification pipeline

email-pipeline.ts
import { AerostackClient } from '@aerostack/sdk'
 
const { sdk } = new AerostackClient({ projectId, apiKey })
 
// Enqueue from your API handler
async function onUserSignup(user: { id: string, email: string, name: string }) {
  // Send welcome email immediately
  await sdk.queue.enqueue('send-email', {
    template: 'welcome',
    to: user.email,
    data: { name: user.name },
  })
 
  // Send onboarding tip after 24 hours
  await sdk.queue.enqueue('send-email', {
    template: 'onboarding-tip-1',
    to: user.email,
    data: { name: user.name },
  }, { delaySeconds: 86400 })
 
  // Send follow-up after 3 days
  await sdk.queue.enqueue('send-email', {
    template: 'onboarding-tip-2',
    to: user.email,
    data: { name: user.name },
  }, { delaySeconds: 259200 })
}
 
// Process jobs in your worker
async function processEmailJob(job: { type: string, payload: any }) {
  const { template, to, data } = job.payload
 
  // Send via your email provider
  await sendEmail({ template, to, data })
 
  // Log in database
  await sdk.db.query(
    'INSERT INTO email_log (id, template, recipient, sent_at) VALUES (?, ?, ?, ?)',
    [crypto.randomUUID(), template, to, Date.now()]
  )
}

Complete example: batch CSV import

csv-import.ts
async function importCSV(userId: string, csvContent: string) {
  const rows = parseCSV(csvContent)
 
  // Enqueue each row as a separate job (can process in parallel)
  for (const row of rows) {
    await sdk.queue.enqueue('import-row', {
      userId,
      data: row,
    }, { maxRetries: 3 })
  }
 
  // Track the import
  await sdk.db.query(
    'INSERT INTO imports (id, user_id, total_rows, status, created_at) VALUES (?, ?, ?, ?, ?)',
    [crypto.randomUUID(), userId, rows.length, 'processing', Date.now()]
  )
}

Queue API reference

MethodSignatureReturns
sdk.queue.enqueue(type: string, payload: any, opts?: { delaySeconds?, maxRetries? }) => Promise<{ jobId: string }>Job ID
sdk.queue.getJob(jobId: string) => Promise<Job>Job details
sdk.queue.listJobs(opts?: { type?, status?, limit? }) => Promise<Job[]>Array of jobs
sdk.queue.cancelJob(jobId: string) => Promise<void>Nothing