Hooks & Custom Logic
Write hooks that intercept lifecycle events (before/after auth, etc.) or create entirely new API endpoints — all with full SDK access and zero server management.
Core concepts
Section titled “Core concepts”Lifecycle hooks
Section titled “Lifecycle hooks”Hooks run at specific moments in the request/response lifecycle:
auth.register.before— validate email domain before account creationauth.login.after— log sign-ins, update last-seen timestampsorder.complete.after— calculate loyalty points, send confirmation
Custom API endpoints
Section titled “Custom API endpoints”Create new endpoints at:
https://api.aerostack.dev/api/v1/custom/<your-endpoint>These endpoints get the full sdk object pre-wired — no boilerplate required.
The SDK in hooks
Section titled “The SDK in hooks”Every hook receives the full sdk object:
| Primitive | SDK call | Why use it in hooks? |
|---|---|---|
| Database | sdk.db.query() | Cross-table joins, custom validation |
| Cache | sdk.cache.set() | Store computed results at the edge |
| Queue | sdk.queue.send() | Offload heavy tasks to background workers |
| AI | sdk.ai.complete() | Categorize input, summarize data in real-time |
| Realtime | sdk.socket.emit() | Notify connected clients on lifecycle events |
Example: validate email on register
Section titled “Example: validate email on register”// Hook: auth.register.beforeexport default async function(sdk, event) { const { email } = event.data
// Block disposable email providers const blocklist = ['mailinator.com', 'tempmail.com', '10minutemail.com'] const domain = email.split('@')[1]
if (blocklist.includes(domain)) { throw new Error('Disposable email addresses are not allowed') }
// Allow everything else return { proceed: true }}Example: custom reporting endpoint
Section titled “Example: custom reporting endpoint”// Custom endpoint: POST /api/v1/custom/analytics/summaryexport default async function(sdk, event) { const { projectId, startDate, endDate } = event.data const cacheKey = `analytics:${projectId}:${startDate}:${endDate}`
const cached = await sdk.cache.get(cacheKey) if (cached) return cached
const [signups, orders, revenue] = await sdk.db.batch([ { sql: 'SELECT COUNT(*) as count FROM users WHERE created_at BETWEEN ? AND ?', params: [startDate, endDate] }, { sql: 'SELECT COUNT(*) as count FROM orders WHERE created_at BETWEEN ? AND ?', params: [startDate, endDate] }, { sql: 'SELECT SUM(total) as total FROM orders WHERE status = ? AND created_at BETWEEN ? AND ?', params: ['paid', startDate, endDate] } ])
const result = { signups: signups.results[0]?.count ?? 0, orders: orders.results[0]?.count ?? 0, revenue: revenue.results[0]?.total ?? 0, generatedAt: Date.now() }
await sdk.cache.set(cacheKey, result, { ttl: 300 })
return result}Workflow
Section titled “Workflow”- Develop — Monaco editor with TypeScript and AI assistance in the dashboard.
- Test — Dry-run with mock payloads in the browser or via CLI.
- Deploy — Push to the global network in under 1 second. No build pipelines.
- Monitor — Trace every execution in the Platform observability dashboard.
Deploy via CLI
Section titled “Deploy via CLI”# Deploy a hook from local fileaerostack deploy hooks/auth-before-register.ts --hook auth.register.before
# Deploy a custom endpointaerostack deploy api/analytics-summary.ts --endpoint analytics/summary