• Platform
  • Solutions
  • Developers
  • Showcase
  • Pricing
  • Company
Get Started
  • Platform

    • Overview
    • Conductor
    • Architecture
    • Security
    • Status

    Capabilities

    • All 276
    • AI & ML
    • Storage
    • Auth & Identity
    • Billing
    • Search
    • Compute
    • Media

    SDK

    • SDK Overview
    • Face Modules
    • Quickstart
    • Playground
    • CLI
  • By Role

    • For Startups
    • For Enterprise
    • For Agencies
    • For Developers
    • For CTOs

    By Industry

    • Fintech
    • E-Commerce
    • Healthcare
    • Media
    • SaaS
    • Education

    By Use Case

    • AI Applications
    • Internal Tools
    • Marketplaces
    • Automation
  • Documentation

    • Getting Started
    • API Reference
    • Authentication
    • Webhooks
    • Templates
    • Sandbox

    Integrations

    • All Integrations
    • Stripe
    • OpenAI
    • AWS
    • GitHub
    • Build Your Own

    Resources

    • Technical Guides
    • Architecture Patterns
    • Project Templates
    • Glossary
    • Platform Changelog
    • SDK Changelog
  • Featured Demos

    • Showcase Gallery
    • SaaS Dashboard
    • AI Chat
    • E-Commerce Store
    • Admin Panel
    • Marketplace

    Case Studies

    • All Case Studies
    • Catalist — Fintech
    • Zumar — E-Commerce
    • Nova — AI SaaS
    • Meridian — Media
    • Atlas — Operations

    Enterprise

    • Enterprise Overview
    • Security
    • Compliance
    • SLA & Uptime
    • Deployment Options
    • Startup Program
  • Plans

    • Overview
    • Compare Plans
    • Pricing Calculator
    • Enterprise Pricing

    Compare

    • EEv3 vs Agencies
    • EEv3 vs No-Code
    • EEv3 vs Freelancers
    • EEv3 vs In-House
    • EEv3 vs Firebase

    Services

    • All Services
    • Custom Development
    • Platform Hosting
    • Consulting
    • AI Integration
  • About

    • About VertexStudio
    • Team
    • Culture
    • The Group
    • Partners
    • Testimonials

    Careers

    • Open Roles
    • Engineering

    More

    • Contact
    • Newsroom
    • Brand Assets
    • Trust Center
    • Legal
  • Get Started
← SDK

Patterns & Recipes

Copy, paste, ship.

Production-ready patterns for common use cases. Each recipe includes the complete code, an explanation of the approach, and which faces are used.

Pattern 01

Auth + RBAC setup

Protect routes by role. Create users with roles, verify sessions, and gate access to faces based on permissions.

ee.auth
auth-rbac.ts
import { ee } from '@/lib/ee'
import { NextRequest, NextResponse } from 'next/server'

// Middleware: verify session + check role
export async function withAuth(
  req: NextRequest,
  requiredRole: 'admin' | 'editor' | 'viewer'
) {
  const token = req.headers.get('Authorization')?.replace('Bearer ', '')
  if (!token) return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })

  const session = await ee.auth.verifySession({ token })
  if (!session.valid) {
    return NextResponse.json({ error: 'Invalid session' }, { status: 401 })
  }

  const user = await ee.auth.getUser({ id: session.userId })
  if (!user.roles.includes(requiredRole)) {
    return NextResponse.json({ error: 'Forbidden' }, { status: 403 })
  }

  return { user, session }
}

// Usage in a route handler
export async function POST(req: NextRequest) {
  const auth = await withAuth(req, 'admin')
  if (auth instanceof NextResponse) return auth

  // auth.user is available — admin-only logic here
  return NextResponse.json({ data: 'secret admin data' })
}
Pattern 02

File upload with preview

Upload files to storage and generate optimised preview thumbnails using media transforms. Presigned URLs for direct browser uploads.

ee.storageee.media
file-upload.ts
import { ee } from '@/lib/ee'

// 1. Get a presigned upload URL (browser uploads directly to storage)
const upload = await ee.storage.createPresignedUpload({
  key: `uploads/${crypto.randomUUID()}.jpg`,
  contentType: 'image/jpeg',
  maxSize: '10mb',
  expiresIn: '15m',
})

// 2. Browser uploads to upload.url (no server proxy needed)
// await fetch(upload.url, { method: 'PUT', body: file })

// 3. Generate a thumbnail preview after upload
const thumbnail = await ee.media.transform({
  source: upload.key,
  output: upload.key.replace('uploads/', 'thumbnails/'),
  operations: [
    { type: 'resize', width: 400, height: 300, fit: 'cover' },
    { type: 'format', format: 'webp', quality: 80 },
  ],
})

// thumbnail.url => CDN URL for the optimised preview
Pattern 03

AI chat with streaming

Stream AI responses to the browser using Server-Sent Events. Token-by-token output with proper connection handling.

ee.ai
ai-streaming.ts
import { ee } from '@/lib/ee'

// API route: stream AI response as SSE
export async function POST(req: Request) {
  const { messages } = await req.json()

  const stream = await ee.ai.generate({
    model: 'claude-sonnet-4-20250514',
    messages,
    stream: true,
  })

  // Return as a ReadableStream (SSE)
  return new Response(
    new ReadableStream({
      async start(controller) {
        for await (const chunk of stream) {
          const data = JSON.stringify({
            text: chunk.text,
            done: chunk.done,
          })
          controller.enqueue(
            new TextEncoder().encode(`data: ${data}\n\n`)
          )
        }
        controller.close()
      },
    }),
    {
      headers: {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        Connection: 'keep-alive',
      },
    }
  )
}
Pattern 04

Webhook handler

Verify webhook signatures, parse typed events, and route to the correct handler. Idempotent processing with deduplication.

ee.webhookee.auth
webhook-handler.ts
import { ee } from '@/lib/ee'
import type { WebhookEvent } from '@evileye/sdk'

export async function POST(req: Request) {
  const body = await req.text()
  const signature = req.headers.get('x-ee-signature')!

  // 1. Verify the webhook signature
  const valid = await ee.webhook.verify({
    body,
    signature,
    secret: process.env.WEBHOOK_SECRET!,
  })
  if (!valid) {
    return new Response('Invalid signature', { status: 401 })
  }

  // 2. Parse the typed event
  const event: WebhookEvent = JSON.parse(body)

  // 3. Route to the correct handler (TypeScript narrows the type)
  switch (event.type) {
    case 'user.created':
      await onUserCreated(event.data) // { userId, email }
      break
    case 'invoice.paid':
      await onInvoicePaid(event.data)  // { invoiceId, amount }
      break
    case 'storage.uploaded':
      await onFileUploaded(event.data) // { key, size, type }
      break
  }

  return new Response('OK', { status: 200 })
}
Pattern 05

Multi-tenant data isolation

Entity-scoped queries ensure tenants never see each other’s data. The SDK enforces isolation at the client level.

ee.databaseee.auth
multi-tenant.ts
import { createClient } from '@evileye/sdk'

// Each tenant gets their own scoped client
function createTenantClient(entityId: string, token: string) {
  return createClient({
    token,
    entity: entityId,
    // All queries are automatically scoped to this entity
    // Cross-entity access is impossible with scoped tokens
  })
}

// In a request handler:
export async function GET(req: Request) {
  const entityId = req.headers.get('x-entity-id')!
  const token = req.headers.get('authorization')!.replace('Bearer ', '')

  const tenant = createTenantClient(entityId, token)

  // This query ONLY returns data for this entity
  const users = await tenant.database.query({
    table: 'users',
    where: { active: true },
    orderBy: { createdAt: 'desc' },
    limit: 50,
  })

  // Even if you omit a WHERE clause, the entity scope applies
  // There is no way to access another tenant\'s data
  return Response.json({ data: users })
}
Pattern 06

Background job with retry

Enqueue background jobs with automatic retry, exponential backoff, and dead-letter handling. Monitor progress in real-time.

ee.queueee.compute
background-job.ts
import { ee } from '@/lib/ee'

// 1. Enqueue a background job
const job = await ee.queue.enqueue({
  queue: 'email-campaigns',
  payload: {
    campaignId: 'camp_123',
    recipientCount: 50_000,
  },
  options: {
    retries: 5,
    backoff: 'exponential',    // 1s, 2s, 4s, 8s, 16s
    deadLetterQueue: 'failed-campaigns',
    timeout: '5m',
  },
})

// 2. Process jobs with a compute function
await ee.compute.createFunction({
  name: 'process-email-campaign',
  queue: 'email-campaigns',
  handler: async (payload) => {
    const campaign = await ee.database.get({
      table: 'campaigns',
      id: payload.campaignId,
    })

    for (const batch of chunk(campaign.recipients, 100)) {
      await ee.email.sendBatch({
        template: campaign.templateId,
        recipients: batch,
      })
    }

    return { sent: campaign.recipients.length }
  },
})

// 3. Check job status
const status = await ee.queue.getJob({ id: job.id })
// status.state => 'completed' | 'processing' | 'failed' | 'queued'

Ready to build your own?

Start with the quickstart, then adapt these patterns to your use case.

Quickstart GuideBrowse All Faces
VertexStudio

The platform behind every company. 276 capabilities through one API.

Platform

  • Overview
  • Conductor
  • Capabilities
  • Architecture
  • Security
  • Status
  • Changelog

SDK & Docs

  • SDK
  • Face Modules
  • Quickstart
  • API Reference
  • Documentation
  • Integrations

Solutions

  • For Startups
  • For Enterprise
  • For Agencies
  • For Developers
  • Showcase
  • Case Studies

Services

  • Custom Development
  • Platform Hosting
  • API Access
  • Consulting
  • AI Integration

Company

  • About
  • Team
  • Careers
  • Partners
  • Newsroom
  • Contact

Legal

  • Privacy Policy
  • Terms of Service
  • DPA
  • Acceptable Use
  • Trust Center
© 2026 VertexStudio. All rights reserved.
Privacy·Terms·Trust Center
Built with EEv3