upstash logo

Optimizing Vercel Functions With Upstash Workflow

This demo shows the cost-saving benefits of using Upstash Workflow for Vercel functions. It compares two methods of calling an image generation API:

Both methods start at the same time and take about the same time to finish. The key difference is the estimated cost per 1M requests (hover for details).

Prompt: A futuristic city skyline at dusk, with towering skyscrapers and flying vehicles in the style of retro sci-fi art. Colors should feature deep purples, bright neon pinks, and glowing electric blues.

Traditional Serverless Function
Total Duration0ms
Vercel Function Duration0ms
Cost for 1M Requests~$0
Vercel Function
import { NextRequest, NextResponse } from 'next/server'
import { ImageResponse } from 'utils/types'

export const POST = async (request: NextRequest) => {
  // get prompt from request
  const params = await request.json()
  const prompt = params.prompt as string

  // make the fetch request
  const response = await fetch(
    "https://api.ideogram.ai/generate",
    {
      method: "POST",
      body: JSON.stringify({
        image_request: {
          model: 'V_2',
          prompt,
          aspect_ratio: 'ASPECT_1_1',
          magic_prompt_option: 'AUTO',
        },
      }),
      headers: {
        'Content-Type': 'application/json',
        'Api-Key': process.env.IDEOGRAM_API_KEY!
      },
    }
  )

  // get the image url
  const payload = await response.json() as ImageResponse
  const url = payload.data[0].url

  return new NextResponse(
    JSON.stringify({ url }),
    { status: 200 }
  )
}
Serverless Function with Upstash Workflow
Total Duration0ms
Vercel Function Duration0ms
Cost for 1M Requests~$0
Workflow Function
import { serve } from '@upstash/workflow/nextjs'
import { Redis } from '@upstash/redis'
import { ImageResponse } from 'utils/types'

const redis = Redis.fromEnv()

export const { POST } = serve<{ prompt: string }>(async (context) => {
  // get prompt from request
  const { prompt } = context.requestPayload

  // make the call to Idogram through QStash
  const { body: result } = await context.call(
    'call Ideogram',
    {
      url: 'https://api.ideogram.ai/generate',
      method: 'POST',
      body: {
        image_request: {
          model: 'V_2',
          prompt,
          aspect_ratio: 'ASPECT_1_1',
          magic_prompt_option: 'AUTO',
        },
      },
      headers: {
        'Content-Type': 'application/json',
        'Api-Key': process.env.IDEOGRAM_API_KEY!,
      },
    }
  ) as { body: ImageResponse };

  // save the image url in redis
  // so that UI can access it
  await context.run('save results in redis', async () => {
    await redis.set<string>(
      context.headers.get('callKey')!,
      result.data[0].url,
      { ex: 120 }, // expire in 120 seconds
    )
  })
})