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 nowFlush all cache
await sdk.cache.flush() // Delete everything -- use with cautionComplete example: session store
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
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
| Method | Signature | Returns |
|---|---|---|
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 }| Status | Description |
|---|---|
pending | Waiting to be processed (or delayed) |
processing | Currently being executed |
completed | Successfully finished |
failed | All retries exhausted |
cancelled | Manually 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
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
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
| Method | Signature | Returns |
|---|---|---|
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 |