Netlify Deployment Beispiele

Netlify deployment Konfigurationsbeispiele einschließlich netlify.toml, edge functions und formularen-behandlung

💻 Netlify Basiskonfiguration toml

🟢 simple

Basis Netlify-konfiguration mit build-einstellungen, weiterleitungen und headers

⏱️ 10 min 🏷️ netlify, configuration, deployment
Prerequisites: Netlify basics, TOML syntax, Static site concepts
[build]
  publish = "build"
  command = "npm run build"
  functions = "functions"

[build.environment]
  NODE_VERSION = "18"
  NPM_VERSION = "9"

[[redirects]]
  from = "/api/*"
  to = "/.netlify/functions/:splat"
  status = 200

[[redirects]]
  from = "/*"
  to = "/index.html"
  status = 200

[[headers]]
  for = "/*"
  [headers.values]
    X-Frame-Options = "DENY"
    X-XSS-Protection = "1; mode=block"
    X-Content-Type-Options = "nosniff"
    Referrer-Policy = "strict-origin-when-cross-origin"

[[headers]]
  for = "/static/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[context.production]
  command = "npm run build:prod"

[context.deploy-preview]
  command = "npm run build:preview"

💻 Netlify Next.js Konfiguration toml

🟡 intermediate ⭐⭐⭐

Optimierte Next.js-anwendungen Netlify-konfiguration mit ISR und edge functions

⏱️ 20 min 🏷️ netlify, next.js, framework
Prerequisites: Next.js, Netlify plugin system, Edge functions
[build]
  publish = ".next"
  command = "npm run build"
  environment = { NODE_VERSION = "18" }

[[plugins]]
  package = "@netlify/plugin-nextjs"

[[redirects]]
  from = "/_next/static/*"
  to = "/_next/static/:splat"
  status = 200
  force = true

[[redirects]]
  from = "/api/*"
  to = "/.netlify/functions/:splat"
  status = 200

[[redirects]]
  from = "/_next/data/*/blog/*.json"
  to = "/.netlify/functions/__ isr-blog?slug=:splat&:splat"
  status = 200

[[redirects]]
  from = "/blog/*"
  to = "/.netlify/functions/__ isr-blog?slug=:splat"
  status = 200

[[headers]]
  for = "/api/*"
  [headers.values]
    Access-Control-Allow-Origin = "*"
    Access-Control-Allow-Methods = "GET, POST, PUT, DELETE, OPTIONS"
    Access-Control-Allow-Headers = "Content-Type, Authorization"

[[headers]]
  for = "/_next/static/*"
  [headers.values]
    Cache-Control = "public, max-age=31536000, immutable"

[[headers]]
  for = "/_next/image/*"
  [headers.values]
    Cache-Control = "public, max-age=86400, must-revalidate"

[context.production.environment]
  NEXT_PUBLIC_API_URL = "https://api.example.com"
  NEXT_PUBLIC_SITE_URL = "https://example.com"

[context.branch-deploy.environment]
  NEXT_PUBLIC_API_URL = "https://dev-api.example.com"
  NEXT_PUBLIC_SITE_URL = "https://dev--example.netlify.app"

[context.deploy-preview.environment]
  NEXT_PUBLIC_API_URL = "https://preview-api.example.com"
  NEXT_PUBLIC_SITE_URL = "https://deploy-preview-1--example.netlify.app"

[functions]
  node_version = "18"

[[functions]]
  directory = "functions"
  node_bundler = "esbuild"

[[edge_functions]]
  function = "geo-redirect"
  path = "/geo-check"

💻 Netlify Edge Functions javascript

🟡 intermediate ⭐⭐⭐⭐

Edge functions beispiele für geolokalisierung, A/B-testing und benutzerdefiniertes middleware

⏱️ 30 min 🏷️ netlify, edge functions, middleware
Prerequisites: JavaScript, Edge computing concepts, Netlify edge functions
// Netlify Edge Functions Examples

// 1. Geolocation-based redirect
// edge-functions/geo-redirect.js
export default async (request, context) => {
  const url = new URL(request.url);
  const country = request.geo?.country?.code?.toLowerCase();

  // Redirect based on country
  if (country === 'us') {
    const usUrl = new URL('/us', url);
    return Response.redirect(usUrl, 302);
  } else if (country === 'uk') {
    const ukUrl = new URL('/uk', url);
    return Response.redirect(ukUrl, 302);
  } else if (country === 'de') {
    const deUrl = new URL('/de', url);
    return Response.redirect(deUrl, 302);
  }

  // Add country data to headers
  const response = await context.next();
  response.headers.set('x-user-country', country || 'unknown');

  return response;
};

// 2. A/B testing edge function
// edge-functions/ab-test.js
export default async (request, context) => {
  const url = new URL(request.url);

  // Skip A/B test for API routes
  if (url.pathname.startsWith('/api/')) {
    return context.next();
  }

  // Check for existing A/B test variant
  const variant = request.headers.get('x-ab-variant') ||
                  Math.random() < 0.5 ? 'A' : 'B';

  // Redirect to variant-specific path
  if (variant === 'B' && !url.pathname.includes('/variant-b')) {
    const variantUrl = new URL('/variant-b' + url.pathname, url);
    const response = Response.redirect(variantUrl, 302);
    response.headers.set('x-ab-variant', variant);
    return response;
  }

  const response = await context.next();
  response.headers.set('x-ab-variant', variant);

  return response;
};

// 3. Authentication middleware
// edge-functions/auth.js
export default async (request, context) => {
  const url = new URL(request.url);

  // Skip auth for public routes
  const publicPaths = ['/login', '/signup', '/forgot-password', '/api/auth'];
  if (publicPaths.some(path => url.pathname.startsWith(path))) {
    return context.next();
  }

  // Check for authentication token
  const authCookie = request.headers.get('cookie')?.split(';')
    ?.find(cookie => cookie.trim().startsWith('auth_token='))
    ?.split('=')[1];

  if (!authCookie) {
    // Redirect to login
    const loginUrl = new URL('/login', url);
    loginUrl.searchParams.set('redirect', url.pathname);
    return Response.redirect(loginUrl, 302);
  }

  // Validate token (mock validation)
  try {
    const isValid = await validateToken(authCookie);
    if (!isValid) {
      throw new Error('Invalid token');
    }

    // Add user info to headers for downstream functions
    const response = await context.next();
    response.headers.set('x-user-authenticated', 'true');
    return response;

  } catch (error) {
    const loginUrl = new URL('/login', url);
    loginUrl.searchParams.set('error', 'invalid_token');
    loginUrl.searchParams.set('redirect', url.pathname);
    return Response.redirect(loginUrl, 302);
  }
};

async function validateToken(token) {
  // Mock validation - replace with real token validation
  return token.length > 10;
}

// 4. Rate limiting edge function
// edge-functions/rate-limit.js
export default async (request, context) => {
  const url = new URL(request.url);
  const clientIP = request.headers.get('x-nf-client-connection-ip') ||
                  request.headers.get('x-forwarded-for') ||
                  'unknown';

  // Simple rate limiting logic (in production, use Redis or similar)
  const RATE_LIMIT = 100; // requests per minute
  const key = `rate-limit-${clientIP}`;

  // This is a mock implementation
  // In production, you'd use a proper rate limiting store
  const now = Date.now();
  const windowStart = now - 60000; // 1 minute ago

  // Check rate limit (mock logic)
  const requestCount = await getRequestCount(key, windowStart);

  if (requestCount >= RATE_LIMIT) {
    return new Response('Rate limit exceeded', {
      status: 429,
      headers: {
        'Retry-After': '60',
        'Content-Type': 'text/plain'
      }
    });
  }

  // Increment request count
  await incrementRequestCount(key);

  const response = await context.next();
  response.headers.set('x-rate-limit-remaining', String(RATE_LIMIT - requestCount));

  return response;
};

// Mock functions for rate limiting (replace with real implementation)
async function getRequestCount(key, windowStart) {
  // Mock implementation
  return Math.floor(Math.random() * 150);
}

async function incrementRequestCount(key) {
  // Mock implementation
  return Promise.resolve();
}

// 5. Content personalization edge function
// edge-functions/personalize.js
export default async (request, context) => {
  const url = new URL(request.url);

  // Skip personalization for API routes
  if (url.pathname.startsWith('/api/')) {
    return context.next();
  }

  // Get user preferences from cookie or headers
  const preferences = getUserPreferences(request);

  const response = await context.next();

  // Inject personalization script
  const personalizationScript = `
    <script>
      window.userPreferences = ${JSON.stringify(preferences)};
      // Trigger personalization
      window.dispatchEvent(new CustomEvent('userPreferencesLoaded', {
        detail: window.userPreferences
      }));
    </script>
  `;

  // Insert script before closing body tag
  const html = await response.text();
  const personalizedHtml = html.replace(
    '</body>',
    personalizationScript + '</body>'
  );

  return new Response(personalizedHtml, {
    headers: response.headers
  });
};

function getUserPreferences(request) {
  const cookieHeader = request.headers.get('cookie') || '';

  // Parse preferences from cookies
  const preferences = {
    theme: 'light',
    language: 'en',
    region: 'us'
  };

  cookieHeader.split(';').forEach(cookie => {
    const [name, value] = cookie.trim().split('=');
    if (name === 'theme') preferences.theme = value || 'light';
    if (name === 'language') preferences.language = value || 'en';
    if (name === 'region') preferences.region = value || 'us';
  });

  // Override with geo data if available
  if (request.geo?.country?.code) {
    preferences.region = request.geo.country.code.toLowerCase();
  }

  return preferences;
}

// 6. Content security policy edge function
// edge-functions/csp.js
export default async (request, context) => {
  const response = await context.next();

  // Set Content Security Policy headers
  const csp = [
    "default-src 'self'",
    "script-src 'self' 'unsafe-inline' 'unsafe-eval' https://cdn.jsdelivr.net https://vercel.live",
    "style-src 'self' 'unsafe-inline' https://fonts.googleapis.com",
    "font-src 'self' https://fonts.gstatic.com",
    "img-src 'self' data: https: blob:",
    "connect-src 'self' https://api.example.com",
    "frame-ancestors 'none'",
    "base-uri 'self'",
    "form-action 'self'"
  ].join('; ');

  response.headers.set('Content-Security-Policy', csp);
  response.headers.set('X-Content-Type-Options', 'nosniff');
  response.headers.set('X-Frame-Options', 'DENY');
  response.headers.set('X-XSS-Protection', '1; mode=block');
  response.headers.set('Referrer-Policy', 'strict-origin-when-cross-origin');

  return response;
};

💻 Netlify Formulare und Analytics html

🔴 complex ⭐⭐⭐⭐

Vollständige formularen-behandlung, analytics-tracking und automatisierte workflows

⏱️ 40 min 🏷️ netlify, forms, analytics, frontend
Prerequisites: HTML, JavaScript, Netlify forms, Analytics
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Netlify Forms & Analytics Example</title>
    <style>
        .form-container {
            max-width: 600px;
            margin: 2rem auto;
            padding: 2rem;
            border: 1px solid #ddd;
            border-radius: 8px;
        }
        .form-group {
            margin-bottom: 1rem;
        }
        label {
            display: block;
            margin-bottom: 0.5rem;
            font-weight: bold;
        }
        input, textarea, select {
            width: 100%;
            padding: 0.5rem;
            border: 1px solid #ccc;
            border-radius: 4px;
        }
        button {
            background: #007cba;
            color: white;
            padding: 0.75rem 1.5rem;
            border: none;
            border-radius: 4px;
            cursor: pointer;
        }
        button:hover {
            background: #005a87;
        }
        .form-netlify {
            display: none;
        }
        .analytics-data {
            background: #f5f5f5;
            padding: 1rem;
            border-radius: 4px;
            margin: 2rem 0;
        }
    </style>
</head>
<body>
    <!-- 1. Contact Form with Netlify Forms -->
    <div class="form-container">
        <h2>Contact Form</h2>
        <form name="contact" method="POST" data-netlify="true" netlify-honeypot="bot-field">
            <!-- Hidden field for spam protection -->
            <input type="hidden" name="form-name" value="contact">
            <div class="form-netlify">
                <label>Don't fill this out if you're human: <input name="bot-field" /></label>
            </div>

            <div class="form-group">
                <label for="name">Name *</label>
                <input type="text" id="name" name="name" required>
            </div>

            <div class="form-group">
                <label for="email">Email *</label>
                <input type="email" id="email" name="email" required>
            </div>

            <div class="form-group">
                <label for="subject">Subject</label>
                <input type="text" id="subject" name="subject">
            </div>

            <div class="form-group">
                <label for="message">Message *</label>
                <textarea id="message" name="message" rows="5" required></textarea>
            </div>

            <div class="form-group">
                <label for="interest">Area of Interest</label>
                <select id="interest" name="interest">
                    <option value="">Select an option</option>
                    <option value="web-development">Web Development</option>
                    <option value="mobile-apps">Mobile Apps</option>
                    <option value="consulting">Consulting</option>
                    <option value="other">Other</option>
                </select>
            </div>

            <div class="form-group">
                <label>
                    <input type="checkbox" name="newsletter" value="yes">
                    Subscribe to newsletter
                </label>
            </div>

            <button type="submit">Send Message</button>
        </form>
    </div>

    <!-- 2. File Upload Form -->
    <div class="form-container">
        <h2>File Upload Form</h2>
        <form name="upload" method="POST" data-netlify="true" enctype="multipart/form-data">
            <input type="hidden" name="form-name" value="upload">

            <div class="form-group">
                <label for="file-name">Name</label>
                <input type="text" id="file-name" name="name" required>
            </div>

            <div class="form-group">
                <label for="file-email">Email</label>
                <input type="email" id="file-email" name="email" required>
            </div>

            <div class="form-group">
                <label for="file">Upload File (max 2MB)</label>
                <input type="file" id="file" name="file" accept=".pdf,.doc,.docx,.txt" required>
            </div>

            <div class="form-group">
                <label for="description">Description</label>
                <textarea id="description" name="description" rows="3"></textarea>
            </div>

            <button type="submit">Upload File</button>
        </form>
    </div>

    <!-- 3. Multi-step Form with Progressive Enhancement -->
    <div class="form-container">
        <h2>Multi-step Registration Form</h2>
        <form name="registration" method="POST" data-netlify="true">
            <input type="hidden" name="form-name" value="registration">

            <!-- Step 1: Basic Information -->
            <div id="step-1" class="form-step">
                <h3>Step 1: Basic Information</h3>
                <div class="form-group">
                    <label for="reg-name">Full Name *</label>
                    <input type="text" id="reg-name" name="full_name" required>
                </div>

                <div class="form-group">
                    <label for="reg-email">Email Address *</label>
                    <input type="email" id="reg-email" name="email_address" required>
                </div>

                <div class="form-group">
                    <label for="reg-phone">Phone Number</label>
                    <input type="tel" id="reg-phone" name="phone_number">
                </div>

                <button type="button" onclick="nextStep(2)">Next</button>
            </div>

            <!-- Step 2: Professional Information -->
            <div id="step-2" class="form-step" style="display: none;">
                <h3>Step 2: Professional Information</h3>
                <div class="form-group">
                    <label for="company">Company</label>
                    <input type="text" id="company" name="company">
                </div>

                <div class="form-group">
                    <label for="position">Position</label>
                    <input type="text" id="position" name="position">
                </div>

                <div class="form-group">
                    <label for="experience">Years of Experience</label>
                    <select id="experience" name="experience">
                        <option value="0-1">0-1 years</option>
                        <option value="2-5">2-5 years</option>
                        <option value="6-10">6-10 years</option>
                        <option value="10+">10+ years</option>
                    </select>
                </div>

                <button type="button" onclick="nextStep(1)">Previous</button>
                <button type="button" onclick="nextStep(3)">Next</button>
            </div>

            <!-- Step 3: Preferences -->
            <div id="step-3" class="form-step" style="display: none;">
                <h3>Step 3: Preferences</h3>
                <div class="form-group">
                    <label for="interests">Areas of Interest</label>
                    <div>
                        <label>
                            <input type="checkbox" name="interests" value="design"> Design
                        </label>
                        <label>
                            <input type="checkbox" name="interests" value="development"> Development
                        </label>
                        <label>
                            <input type="checkbox" name="interests" value="marketing"> Marketing
                        </label>
                    </div>
                </div>

                <div class="form-group">
                    <label for="hear-about">How did you hear about us?</label>
                    <select id="hear-about" name="hear_about">
                        <option value="">Select an option</option>
                        <option value="search">Search Engine</option>
                        <option value="social">Social Media</option>
                        <option value="referral">Referral</option>
                        <option value="advertisement">Advertisement</option>
                        <option value="other">Other</option>
                    </select>
                </div>

                <div class="form-group">
                    <label for="comments">Additional Comments</label>
                    <textarea id="comments" name="comments" rows="4"></textarea>
                </div>

                <button type="button" onclick="nextStep(2)">Previous</button>
                <button type="submit">Submit Registration</button>
            </div>
        </form>
    </div>

    <!-- 4. Analytics Data Display -->
    <div class="analytics-data">
        <h3>Real-time Analytics Data</h3>
        <div id="visitor-info">
            <p><strong>Location:</strong> <span id="location">Loading...</span></p>
            <p><strong>Device:</strong> <span id="device">Loading...</span></p>
            <p><strong>Browser:</strong> <span id="browser">Loading...</span></p>
            <p><strong>Session ID:</strong> <span id="session-id">Loading...</span></p>
        </div>
    </div>

    <!-- Netlify Analytics Script -->
    <script>
        // Initialize Netlify Analytics
        if (typeof netlifyAnalytics !== 'undefined') {
            netlifyAnalytics.on('routeChange', ({ path }) => {
                console.log('Route changed to:', path);
            });
        }
    </script>

    <!-- 5. Form Handling Script -->
    <script>
        // Multi-step form logic
        function nextStep(stepNumber) {
            // Hide all steps
            document.querySelectorAll('.form-step').forEach(step => {
                step.style.display = 'none';
            });

            // Show selected step
            document.getElementById(`step-${stepNumber}`).style.display = 'block';
        }

        // Form submission handling
        document.querySelectorAll('form').forEach(form => {
            form.addEventListener('submit', function(e) {
                // Show loading state
                const submitBtn = this.querySelector('button[type="submit"]');
                const originalText = submitBtn.textContent;
                submitBtn.textContent = 'Submitting...';
                submitBtn.disabled = true;

                // Track form submission
                if (typeof netlifyAnalytics !== 'undefined') {
                    netlifyAnalytics.track({
                        eventName: 'form_submission',
                        data: {
                            formName: this.getAttribute('name'),
                            timestamp: new Date().toISOString()
                        }
                    });
                }

                // Let the form submit normally to Netlify
                setTimeout(() => {
                    submitBtn.textContent = originalText;
                    submitBtn.disabled = false;
                }, 3000);
            });
        });

        // Load visitor information
        async function loadVisitorInfo() {
            try {
                // Simulate visitor data (in real app, this would come from Netlify Functions)
                const visitorData = {
                    location: await getLocation(),
                    device: getDeviceInfo(),
                    browser: getBrowserInfo(),
                    sessionId: getSessionId()
                };

                document.getElementById('location').textContent = visitorData.location;
                document.getElementById('device').textContent = visitorData.device;
                document.getElementById('browser').textContent = visitorData.browser;
                document.getElementById('session-id').textContent = visitorData.sessionId;
            } catch (error) {
                console.error('Error loading visitor info:', error);
            }
        }

        async function getLocation() {
            // In production, you'd use Netlify Functions to get geo data
            // For now, return a mock location
            return 'San Francisco, CA, USA';
        }

        function getDeviceInfo() {
            const userAgent = navigator.userAgent;
            if (/Mobile|Android|iPhone|iPad/.test(userAgent)) {
                return 'Mobile';
            } else if (/Tablet/.test(userAgent)) {
                return 'Tablet';
            } else {
                return 'Desktop';
            }
        }

        function getBrowserInfo() {
            const userAgent = navigator.userAgent;
            if (userAgent.includes('Chrome')) return 'Chrome';
            if (userAgent.includes('Firefox')) return 'Firefox';
            if (userAgent.includes('Safari')) return 'Safari';
            if (userAgent.includes('Edge')) return 'Edge';
            return 'Unknown';
        }

        function getSessionId() {
            // Get or create session ID from local storage
            let sessionId = localStorage.getItem('netlify_session_id');
            if (!sessionId) {
                sessionId = 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
                localStorage.setItem('netlify_session_id', sessionId);
            }
            return sessionId;
        }

        // Load visitor info when page loads
        document.addEventListener('DOMContentLoaded', loadVisitorInfo);

        // Track page view
        if (typeof netlifyAnalytics !== 'undefined') {
            netlifyAnalytics.track({
                eventName: 'page_view',
                data: {
                    path: window.location.pathname,
                    title: document.title,
                    timestamp: new Date().toISOString()
                }
            });
        }

        // Track form interactions
        document.querySelectorAll('input, textarea, select').forEach(field => {
            field.addEventListener('focus', function() {
                if (typeof netlifyAnalytics !== 'undefined') {
                    netlifyAnalytics.track({
                        eventName: 'form_field_focus',
                        data: {
                            fieldName: this.name,
                            fieldType: this.type,
                            timestamp: new Date().toISOString()
                        }
                    });
                }
            });
        });
    </script>
</body>
</html>