How the Vercel Function Proxies Requests to the GPT Image 2 API: Complete Implementation Guide

The Vercel Function at api/generate-image.js acts as a secure, authenticated proxy that validates users, reserves generation credits, forwards requests to the GPT Image 2 API at ciyuan.today, and returns base64-encoded images to the client.

The freestylefly/awesome-gpt-image-2 repository implements a serverless architecture where all GPT Image 2 API calls are mediated through a Vercel Function. This design pattern protects upstream credentials, enforces per-user rate limits, and provides consistent error handling. The proxy integrates with Supabase for authentication and usage accounting, making it suitable for production multi-tenant applications.

Core Responsibilities of the Vercel Function Proxy

The proxy handles four critical concerns before any image generation occurs. Each responsibility maps to specific functions in the codebase, with clear separation between authentication, authorization, upstream communication, and response formatting.

Configuration and Authentication Validation

The function first verifies that required secrets are present and that the requesting user is authenticated. In api/generate-image.js, the isServerConfigured() helper checks for CIYUAN_API_KEY and SUPABASE_SERVICE_ROLE_KEY environment variables. The getAuthContext function from api/_lib/supabase.js validates the Bearer token against Supabase and returns the authenticated user or an anonymous context for GET requests.

Anonymous access is restricted to fetching current authentication status. All POST requests—which trigger actual image generation—require a valid Supabase JWT in the Authorization header.

Generation Slot Reservation

Before calling the upstream API, the proxy reserves a generation slot to prevent abuse and enforce credit limits. Two code paths exist based on user type:

  • reserveGeneration — Standard users trigger the reserve_generation_usage RPC in Supabase, which atomically checks available credits and increments counters.
  • reserveCreditGeneration — Super-admin users bypass credit checks via a separate path, though usage is still logged.

The reservation returns a reservationId, the amount of credits to be deducted, and updated free-generation counters. This reservation is held in a pending state until the upstream call completes or fails.

Upstream API Communication

The generateImage(prompt) function constructs the request to the GPT Image 2 service. It uses process.env.CIYUAN_BASE_URL (defaulting to https://ciyuan.today) and sends a POST to /v1/images/generations.

// Request structure sent to ciyuan.today
{
  model: 'gpt-image-2',
  prompt: 'user-provided prompt',
  size: '1024x1024',    // or other supported dimensions
  quality: 'standard',  // or 'hd'
  format: 'b64_json'    // returns base64-encoded JPEG
}

The upstream API key is never exposed to clients—it resides solely in Vercel environment variables and is injected server-side via Authorization: Bearer ${process.env.CIYUAN_API_KEY}.

Response Handling and Cleanup

Successful responses from the GPT Image 2 API contain a base64-encoded JPEG in payload.data[0].b64_json. The proxy converts this to a data-URL format (data:image/jpeg;base64,...) suitable for immediate use in <img> tags, then marks the reservation complete via completeReservation.

On any failure—whether network error, upstream timeout, or HTTP 429 rate limit—the proxy catches the exception and releases the reservation with an appropriate error code:

const errorCode = error?.status === 429 ? 'UPSTREAM_BUSY' : 'GENERATION_FAILED';
await releaseReservation(auth.client, reservation.reservationId, errorCode);

This ensures credits are not deducted for failed generations and clients receive actionable error messages in a uniform JSON format.

Complete Proxy Implementation

The full request lifecycle in api/generate-image.js demonstrates how these stages compose into a robust handler:

export default async function handler(req, res) {
  // 1️⃣ Validate request method
  if (!['GET', 'POST'].includes(req.method)) {
    res.setHeader('Allow', 'GET, POST');
    return json(res, 405, { ok: false, error: 'METHOD_NOT_ALLOWED' });
  }

  // 2️⃣ Ensure server is configured
  if (!isServerConfigured()) {
    return json(res, 500, { ok: false, error: 'SERVER_NOT_CONFIGURED' });
  }

  // 3️⃣ Authenticate via Supabase
  const auth = await getAuthContext(req, {
    allowAnonymous: req.method === 'GET'
  });
  if (auth.error) {
    return json(res, 401, { ok: false, error: 'UNAUTHORIZED' });
  }

  // GET requests only fetch auth status
  if (req.method === 'GET') {
    return json(res, 200, { ok: true, user: auth.profile });
  }

  const { prompt, caseId } = req.body;

  // 4️⃣ Reserve a generation slot
  const reservation = auth.profile.isSuperAdmin
    ? await reserveCreditGeneration(auth.client, auth.user.id, caseId, prompt)
    : await reserveGeneration(auth.client, auth.user.id, caseId, prompt);

  if (!reservation.success) {
    return json(res, 429, { ok: false, error: 'INSUFFICIENT_CREDITS' });
  }

  try {
    // 5️⃣ Call the upstream GPT Image 2 API
    const image = await generateImage(prompt);

    // 6️⃣ Mark reservation complete and return result
    await completeReservation(auth.client, reservation.reservationId);

    return json(res, 200, {
      ok: true,
      image,
      user: profileFromReservation(auth.profile, reservation)
    });
  } catch (error) {
    const errorCode = error?.status === 429 ? 'UPSTREAM_BUSY' : 'GENERATION_FAILED';

    await releaseReservation(auth.client, reservation.reservationId, errorCode);

    return json(res, error?.status || 500, {
      ok: false,
      error: errorCode,
      message: error.message
    });
  }
}

Client Integration Pattern

Front-end applications interact with the proxy through a simple fetch pattern. The client never handles raw API keys or manages credit accounting directly:

fetch('/api/generate-image', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/json',
    Authorization: `Bearer ${accessToken}`   // Supabase JWT
  },
  body: JSON.stringify({
    prompt: 'A futuristic city at sunset',
    caseId: 12345
  })
})
  .then(r => r.json())
  .then(data => {
    if (data.ok) {
      // `data.image` is a data-URL ready for <img src=…>
      document.querySelector('img').src = data.image;
    } else {
      console.error('Generation error:', data.error);
    }
  });

Key Files and Their Roles

File Purpose
api/generate-image.js Main Vercel Function implementing the proxy logic
api/_lib/supabase.js Authentication helpers, reservation management, and profile utilities
scripts/vite-local-api.mjs Local development server that emulates Vercel Functions behavior
README.md Environment variable documentation and deployment instructions

Security and Architectural Guarantees

The Vercel Function proxy design provides specific protections:

  • Credential isolation: The CIYUAN_API_KEY never leaves the serverless environment
  • Authenticated access: All generation requests are tied to verified Supabase users
  • Usage enforcement: The reservation system prevents credit overdraft and enables quota tracking
  • Graceful degradation: 429 responses from the upstream API are caught and surfaced with UPSTREAM_BUSY, allowing client-side retry logic

Summary

  • The Vercel Function proxy at api/generate-image.js mediates all GPT Image 2 API traffic, protecting credentials and enforcing business logic
  • Four-stage processing—configuration check, authentication, reservation, upstream call—ensures reliable quota management
  • Environment variables CIYUAN_API_KEY and SUPABASE_SERVICE_ROLE_KEY are the only secrets required for deployment
  • Base64 image delivery eliminates additional storage costs and enables immediate client-side rendering
  • Reservation cleanup on failure guarantees users are not charged for unsuccessful generations

Frequently Asked Questions

What happens if the GPT Image 2 API returns a 429 rate limit?

The proxy catches HTTP 429 responses, releases the user's reservation with error code UPSTREAM_BUSY, and returns a 429 response to the client. This allows front-end code to implement exponential backoff retry logic without consuming user credits.

Can anonymous users generate images through this proxy?

No. While anonymous GET requests are permitted to check authentication status, all POST requests that trigger actual image generation require a valid Supabase JWT. The getAuthContext function enforces this distinction based on request method.

How does the proxy handle the API key without exposing it to clients?

The upstream CIYUAN_API_KEY is stored exclusively in Vercel environment variables and injected server-side by the generateImage function. Client-side code only sends its own Supabase JWT for authentication—the GPT Image 2 credentials never appear in browser network traffic or JavaScript bundles.

What is the purpose of the reservation system in Supabase?

The reservation pattern provides atomic credit checking and usage accounting. By creating a pending reservation before calling the upstream API, the system guarantees that users cannot exceed their allocated credits even under race conditions. The reservation is either completed on success or released on failure, maintaining accurate billing state.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →