Intermediate25 minModule 4 of 4

Ship It

Deploy your AI Image Studio to production — environment variables, Vercel deployment, webhook configuration, and monitoring.

Pre-deployment checklist

Before deploying, verify these are in place:

  • API key stored in environment variable, not in source code
  • Webhook secret set and verified in the handler
  • CDN output URLs downloaded and persisted to Blob storage
  • Database migrations applied (npx prisma db push)
  • CORS headers configured for your domain
  • Error states handled in the UI

Deploy to Vercel

# Install Vercel CLI
npm i -g vercel

# Deploy (first run creates the project)
vercel

# Or deploy straight to production
vercel --prod

Environment variables in Vercel

In the Vercel dashboard, go to Settings → Environment Variables and add:

VariableValueEnvironments
SKYTELLS_API_KEYsk-prod-...Production only
SKYTELLS_WEBHOOK_SECRETFrom Skytells DashboardProduction only
DATABASE_URLYour Postgres URLAll
BLOB_READ_WRITE_TOKENFrom Vercel BlobAll
NEXT_PUBLIC_APP_URLhttps://yourapp.vercel.appProduction

Set separate SKYTELLS_API_KEY values for preview and production environments.

Configure the webhook URL

After deploying, go to the Skytells Dashboard → API Keys → Webhooks and register:

https://yourapp.vercel.app/api/webhooks

Events to subscribe: prediction.completed

Copy the webhook signing secret and set it as SKYTELLS_WEBHOOK_SECRET in Vercel.

Database setup (Neon / Supabase)

Both Neon and Supabase offer free-tier Postgres with direct connection strings:

# After setting DATABASE_URL in Vercel environment
npx prisma db push

For connection pooling in a serverless environment:

DATABASE_URL="postgresql://...?pgbouncer=true&connection_limit=1"

Testing webhooks locally

Use the Skytells webhook testing tool or a forwarding service during development:

# Using ngrok
ngrok http 3000

# Then set in .env.local:
NEXT_PUBLIC_APP_URL=https://your-id.ngrok.io

Or test the webhook handler directly with a curl:

PAYLOAD='{"id":"pred_test","status":"succeeded","output":["https://cdn.skytells.ai/test.png"]}'
SIG=$(echo -n "$PAYLOAD" | openssl dgst -sha256 -hmac "$SKYTELLS_WEBHOOK_SECRET" | cut -d' ' -f2)

curl -X POST http://localhost:3000/api/webhooks \
  -H "Content-Type: application/json" \
  -H "x-skytells-signature: sha256=$SIG" \
  -d "$PAYLOAD"

Monitoring

Track key metrics

// lib/analytics.ts
export function trackGeneration(event: {
  predictionId: string;
  model: string;
  status: 'created' | 'succeeded' | 'failed';
  latencyMs?: number;
}) {
  // Send to your analytics (Posthog, Mixpanel, etc.)
  console.log('[generation]', event);
}

Vercel's built-in observability

Enable Vercel Analytics and Speed Insights in your project settings — both are free for hobby tier and give you:

  • Request latency per route
  • Error rate by route
  • Real user performance data

Budget guardrails

Set spending limits in the Skytells Dashboard → Billing → Spend Limits to prevent unexpected charges during load testing or after a traffic spike.

Rate limiting

Add rate limiting to your generate endpoint to protect against abuse:

// middleware.ts
import { NextRequest, NextResponse } from 'next/server';

const REQUESTS_PER_MINUTE = 5;
const requestCounts = new Map<string, { count: number; resetAt: number }>();

export function middleware(req: NextRequest) {
  if (!req.nextUrl.pathname.startsWith('/api/generate')) {
    return NextResponse.next();
  }

  const ip = req.headers.get('x-forwarded-for') ?? 'unknown';
  const now = Date.now();
  const entry = requestCounts.get(ip);

  if (!entry || entry.resetAt < now) {
    requestCounts.set(ip, { count: 1, resetAt: now + 60_000 });
    return NextResponse.next();
  }

  if (entry.count >= REQUESTS_PER_MINUTE) {
    return NextResponse.json({ error: 'Rate limit exceeded' }, { status: 429 });
  }

  entry.count++;
  return NextResponse.next();
}

What to build next

Now that your Image Studio is live, here are natural extensions:

FeatureComplexityImpact
User authentication (Clerk)LowHigh — enables personalized history
Prompt suggestions / examplesLowMedium — reduces blank canvas
Image variations (img2img)MediumHigh — power user feature
Public galleryMediumHigh — viral / social
Inpainting / editingHighHigh — differentiator
Subscription billing (Stripe)MediumHigh — monetization

Congratulations

You've built and shipped a production AI Image Studio. Along the way you learned:

  1. Architecture planning before writing code
  2. Backend: prediction creation, webhook handling, persistent storage
  3. Frontend: optimistic UI, real-time polling, component composition
  4. Production: deployment, environment config, monitoring

Next paths:

On this page