Exemples Vercel Deployment

Exemples de configuration Vercel deployment incluant vercel.json, serverless functions et edge functions

💻 Configuration Vercel de Base json

🟢 simple

Configuration Vercel de base avec domaines personnalisés, paramètres de build et variables d'environnement

⏱️ 10 min 🏷️ vercel, configuration, deployment
Prerequisites: Vercel basics, JavaScript/TypeScript
{
  "version": 2,
  "name": "my-app",
  "builds": [
    {
      "src": "package.json",
      "use": "@vercel/static-build",
      "config": {
        "distDir": "build"
      }
    }
  ],
  "routes": [
    {
      "src": "/(.*)",
      "dest": "/build/$1"
    }
  ],
  "env": {
    "NODE_ENV": "production"
  },
  "build": {
    "env": {
      "CUSTOM_KEY": "value"
    }
  },
  "functions": {
    "pages/api/**/*.js": {
      "maxDuration": 10
    }
  }
}

💻 Configuration Vercel Next.js json

🟡 intermediate ⭐⭐⭐

Configuration Next.js avancée avec ISR, optimisation d'images et support monorepo

⏱️ 20 min 🏷️ vercel, next.js, framework
Prerequisites: Next.js, Vercel deployment, JavaScript/TypeScript
{
  "version": 2,
  "name": "nextjs-app",
  "buildCommand": "npm run build",
  "outputDirectory": ".next",
  "installCommand": "npm install",
  "framework": "nextjs",
  "builds": [
    {
      "src": "package.json",
      "use": "@vercel/next"
    }
  ],
  "functions": {
    "pages/api/**/*.js": {
      "maxDuration": 30,
      "memory": 1024
    }
  },
  "rewrites": [
    {
      "source": "/blog/:path*",
      "destination": "/blog?slug=:path*"
    },
    {
      "source": "/((?!api|_next/static|_next/image|favicon.ico).*)",
      "destination": "/pages/$1"
    }
  ],
  "redirects": [
    {
      "source": "/old-path/:path*",
      "destination": "/new-path/:path*",
      "permanent": true
    }
  ],
  "headers": [
    {
      "source": "/api/(.*)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "s-maxage=86400"
        },
        {
          "key": "Access-Control-Allow-Origin",
          "value": "*"
        }
      ]
    },
    {
      "source": "/(.*)\.(js|css|png|jpg|jpeg|gif|ico|svg)",
      "headers": [
        {
          "key": "Cache-Control",
          "value": "public, max-age=31536000, immutable"
        }
      ]
    }
  ],
  "images": {
    "domains": ["example.com", "cdn.example.com"],
    "sizes": [16, 32, 48, 64, 96, 128, 256, 384],
    "minimumCacheTTL": 60,
    "format": ["image/webp", "image/avif"]
  }
}

💻 Vercel Serverless Functions javascript

🟡 intermediate ⭐⭐⭐

Exemples de serverless functions avec différents runtimes et cas d'usage

⏱️ 25 min 🏷️ vercel, functions, serverless, api
Prerequisites: JavaScript/TypeScript, API development, Serverless concepts
// Vercel Serverless Functions Examples

// 1. API Route with Node.js (Next.js)
// pages/api/users.js
export default async function handler(req, res) {
  if (req.method === 'GET') {
    try {
      // Fetch users from database
      const response = await fetch('https://jsonplaceholder.typicode.com/users');
      const users = await response.json();

      res.status(200).json(users);
    } catch (error) {
      res.status(500).json({ error: 'Failed to fetch users' });
    }
  } else if (req.method === 'POST') {
    try {
      const user = req.body;

      // Validate input
      if (!user.name || !user.email) {
        return res.status(400).json({ error: 'Name and email are required' });
      }

      // Create user logic here
      const newUser = {
        id: Date.now(),
        ...user,
        createdAt: new Date().toISOString()
      };

      res.status(201).json(newUser);
    } catch (error) {
      res.status(500).json({ error: 'Failed to create user' });
    }
  } else {
    res.setHeader('Allow', ['GET', 'POST']);
    res.status(405).end('Method Not Allowed');
  }
}

// 2. Edge Function with TypeScript
// api/edge-function.ts
import { NextRequest, NextResponse } from 'next/server';

export const config = {
  runtime: 'edge'
};

export default function handler(req: NextRequest) {
  const url = req.nextUrl;
  const searchParams = url.searchParams;

  // Get IP address and user agent
  const ip = req.ip || req.headers.get('x-forwarded-for');
  const userAgent = req.headers.get('user-agent') || '';

  // Get geolocation from headers
  const country = req.headers.get('x-vercel-ip-country') || 'Unknown';
  const region = req.headers.get('x-vercel-ip-country-region') || 'Unknown';

  // Response with location data
  const response = {
    ip,
    userAgent,
    location: {
      country,
      region,
      city: req.headers.get('x-vercel-ip-city') || 'Unknown'
    },
    timestamp: new Date().toISOString()
  };

  return NextResponse.json(response, {
    status: 200,
    headers: {
      'Cache-Control': 's-maxage=60, stale-while-revalidate=300',
      'Access-Control-Allow-Origin': '*'
    }
  });
}

// 3. Background Function
// api/background-task.js
export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  try {
    const { email, data } = req.body;

    // Start background processing
    setTimeout(async () => {
      try {
        // Send email
        await fetch('https://api.resend.com/emails', {
          method: 'POST',
          headers: {
            'Authorization': `Bearer ${process.env.RESEND_API_KEY}`,
            'Content-Type': 'application/json'
          },
          body: JSON.stringify({
            from: '[email protected]',
            to: [email],
            subject: 'Your background task is complete',
            html: `<p>Your data has been processed: ${JSON.stringify(data, null, 2)}</p>`
          })
        });

        console.log('Background task completed successfully');
      } catch (error) {
        console.error('Background task failed:', error);
      }
    }, 0);

    res.status(202).json({
      message: 'Background task started',
      taskId: Date.now()
    });

  } catch (error) {
    res.status(500).json({ error: 'Failed to start background task' });
  }
}

// 4. Cron Job Function
// api/cron-job.js
export default async function handler(req, res) {
  // Verify cron secret
  const cronSecret = req.headers['x-vercel-cron-secret'];
  if (cronSecret !== process.env.CRON_SECRET) {
    return res.status(401).json({ error: 'Unauthorized' });
  }

  try {
    // Perform scheduled tasks
    const tasks = await Promise.allSettled([
      cleanupOldFiles(),
      sendDailyReports(),
      updateAnalytics()
    ]);

    const results = tasks.map((task, index) => ({
      task: ['cleanup', 'reports', 'analytics'][index],
      status: task.status,
      value: task.status === 'fulfilled' ? task.value : task.reason.message
    }));

    res.status(200).json({
      message: 'Cron job completed',
      timestamp: new Date().toISOString(),
      results
    });

  } catch (error) {
    res.status(500).json({ error: 'Cron job failed' });
  }
}

async function cleanupOldFiles() {
  // Cleanup logic here
  return 'Cleaned up 125 old files';
}

async function sendDailyReports() {
  // Report sending logic here
  return 'Sent 45 daily reports';
}

async function updateAnalytics() {
  // Analytics update logic here
  return 'Updated analytics data';
}

// 5. Webhook Handler
// api/webhook.js
import crypto from 'crypto';

export default async function handler(req, res) {
  if (req.method !== 'POST') {
    return res.status(405).json({ error: 'Method not allowed' });
  }

  const signature = req.headers['x-webhook-signature'];
  const secret = process.env.WEBHOOK_SECRET;

  // Verify webhook signature
  if (!verifySignature(req.body, signature, secret)) {
    return res.status(401).json({ error: 'Invalid signature' });
  }

  try {
    const event = JSON.parse(req.body);

    switch (event.type) {
      case 'user.created':
        await handleUserCreated(event.data);
        break;
      case 'payment.completed':
        await handlePaymentCompleted(event.data);
        break;
      default:
        console.log('Unknown event type:', event.type);
    }

    res.status(200).json({ received: true });

  } catch (error) {
    res.status(500).json({ error: 'Failed to process webhook' });
  }
}

function verifySignature(payload, signature, secret) {
  const hmac = crypto.createHmac('sha256', secret);
  hmac.update(payload);
  const expectedSignature = hmac.digest('hex');
  return crypto.timingSafeEqual(
    Buffer.from(signature, 'hex'),
    Buffer.from(expectedSignature, 'hex')
  );
}

async function handleUserCreated(userData) {
  console.log('User created:', userData);
  // Handle user creation logic
}

async function handlePaymentCompleted(paymentData) {
  console.log('Payment completed:', paymentData);
  // Handle payment logic
}

💻 Configuration Vercel Monorepo json

🔴 complex ⭐⭐⭐⭐

Setup monorepo avec applications multiples, paquets partagés et gestion de workspaces

⏱️ 35 min 🏷️ vercel, monorepo, workspace
Prerequisites: Monorepo concepts, Vercel advanced, Turborepo/NX
{
  "version": 2,
  "name": "monorepo-app",
  "builds": [
    {
      "src": "apps/web/package.json",
      "use": "@vercel/next",
      "config": {
        "distDir": "build"
      }
    },
    {
      "src": "apps/api/package.json",
      "use": "@vercel/static-build",
      "config": {
        "outputDirectory": "dist"
      }
    },
    {
      "src": "apps/docs/package.json",
      "use": "@vercel/static-build",
      "config": {
        "outputDirectory": ".vitepress/dist"
      }
    }
  ],
  "installCommand": "npm install --workspaces --if-present",
  "routes": [
    {
      "src": "/api/(.*)",
      "dest": "/apps/api/$1"
    },
    {
      "src": "/docs/(.*)",
      "dest": "/apps/docs/$1"
    },
    {
      "handle": "filesystem"
    },
    {
      "src": "/(.*)",
      "dest": "/apps/web/$1"
    }
  ],
  "functions": {
    "apps/web/pages/api/**/*.js": {
      "maxDuration": 30
    },
    "apps/api/dist/**/*.js": {
      "maxDuration": 60
    }
  },
  "env": {
    "NODE_ENV": "production",
    "TURBOREPO_CACHE": "BLOB"
  },
  "build": {
    "env": {
      "VERCEL": "1",
      "TURBO_TOKEN": "@vercel/turbopack-token"
    }
  },
  "regions": ["iad1", "sfo1"],
  "framework": null
}