# Rate Limiting Guide for Edge Functions

This guide helps you choose the appropriate rate limit tier for each edge function and explains how to implement rate limiting consistently across the application.

## Quick Reference

### Rate Limit Tiers

| Tier | Requests/Min | Use Case |
|------|--------------|----------|
| **STRICT** | 5 | Expensive operations (uploads, exports, batch processing) |
| **MODERATE** | 10 | Moderation actions, content submission, security operations |
| **STANDARD** | 20 | Typical read/write operations, account management |
| **LENIENT** | 30 | Lightweight reads, public data, validation |
| **GENEROUS** | 60 | High-frequency operations (webhooks, polling, health checks) |

### Per-User Tiers (Rate limits by user ID instead of IP)

| Tier | Requests/Min | Use Case |
|------|--------------|----------|
| **PER_USER_STRICT** | 5 | User-specific expensive operations |
| **PER_USER_MODERATE** | 10 | User-specific moderation actions |
| **PER_USER_STANDARD** | 20 | User-specific standard operations |
| **PER_USER_LENIENT** | 40 | User-specific frequent operations |

## How to Implement Rate Limiting

### Basic Implementation

```typescript
import { serve } from 'https://deno.land/std@0.168.0/http/server.ts';
import { corsHeaders } from '../_shared/cors.ts';
import { rateLimiters, withRateLimit } from '../_shared/rateLimiter.ts';

// Your handler function
const handler = async (req: Request): Promise<Response> => {
  // Your edge function logic here
  return new Response(JSON.stringify({ success: true }), {
    headers: { ...corsHeaders, 'Content-Type': 'application/json' }
  });
};

// Apply rate limiting with appropriate tier
serve(withRateLimit(handler, rateLimiters.moderate, corsHeaders));
```

### Per-User Rate Limiting

```typescript
// Rate limit by user ID instead of IP address
serve(withRateLimit(handler, rateLimiters.perUserModerate, corsHeaders));
```

### Custom Rate Limiting

```typescript
import { createRateLimiter } from '../_shared/rateLimiter.ts';

// Create a custom rate limiter
const customLimiter = createRateLimiter({
  windowMs: 60000,
  maxRequests: 15,
  keyGenerator: (req) => {
    // Custom key logic
    return req.headers.get('x-custom-key') || 'default';
  }
});

serve(withRateLimit(handler, customLimiter, corsHeaders));
```

## Recommended Rate Limits by Function Category

### 🔴 STRICT (5 req/min)

**Currently Implemented:**
- ✅ `upload-image` - CloudFlare image upload

**Recommended:**
- `export-user-data` - Data export operations
- Any function that makes expensive external API calls
- Batch data processing operations
- Functions that manipulate large datasets

### 🟠 MODERATE (10 req/min)

**Currently Implemented:**
- ✅ `process-selective-approval` - Moderation approvals
- ✅ `process-selective-rejection` - Moderation rejections

**Recommended:**
- `admin-delete-user` - Admin user deletion
- `manage-moderator-topic` - Admin moderation management
- `merge-contact-tickets` - Admin ticket management
- `mfa-unenroll` - Security operations
- `resend-deletion-code` - Prevent code spam
- `send-escalation-notification` - Admin escalations
- `send-password-added-email` - Security emails
- User submission functions (parks, rides, edits)

### 🟡 STANDARD (20 req/min)

**Recommended:**
- `cancel-account-deletion` - Account management
- `cancel-email-change` - Account management
- `confirm-account-deletion` - Account management
- `request-account-deletion` - Account management
- `create-novu-subscriber` - User registration
- `send-contact-message` - Contact form submissions
- Email validation functions
- Authentication-related functions

### 🟢 LENIENT (30 req/min)

**Recommended:**
- `detect-location` - Lightweight location lookup
- `check-transaction-status` - Status polling
- `validate-email-backend` - Email validation
- `sitemap` - Public sitemap generation
- Read-only public endpoints

### 🔵 GENEROUS (60 req/min)

**Recommended:**
- `novu-webhook` - External webhook receiver
- `scheduled-maintenance` - Health checks
- Internal service-to-service communication
- Real-time status endpoints

### ⚫ NO RATE LIMITING NEEDED

These functions are typically called internally or on a schedule:
- `cleanup-old-versions` - Scheduled cleanup
- `process-expired-bans` - Scheduled task
- `process-scheduled-deletions` - Scheduled task
- `run-cleanup-jobs` - Scheduled task
- `migrate-novu-users` - One-time migration
- Internal notification functions (notify-*)
- `seed-test-data` - Development only

## Best Practices

### 1. Choose the Right Tier

- **Start restrictive**: Begin with a lower tier and increase if needed
- **Consider cost**: Match the rate limit to the operation's resource cost
- **Think about abuse**: Higher abuse risk = stricter limits
- **Monitor usage**: Use edge function logs to track rate limit hits

### 2. Use Per-User Limits for Authenticated Endpoints

```typescript
// ✅ Good: Rate limit authenticated operations per user
serve(withRateLimit(handler, rateLimiters.perUserModerate, corsHeaders));

// ❌ Less effective: Rate limit authenticated operations per IP
// (Multiple users behind same IP can hit each other's limits)
serve(withRateLimit(handler, rateLimiters.moderate, corsHeaders));
```

### 3. Handle Rate Limit Errors Gracefully

Rate limit responses automatically include:
- `429 Too Many Requests` status code
- `Retry-After` header (seconds to wait)
- `X-RateLimit-Limit` header (max requests allowed)
- `X-RateLimit-Remaining` header (requests remaining)

### 4. Document Your Choice

Always add a comment explaining why you chose a specific tier:

```typescript
// Apply moderate rate limiting (10 req/min) for moderation actions
// to prevent abuse while allowing legitimate moderator workflows
serve(withRateLimit(handler, rateLimiters.moderate, corsHeaders));
```

### 5. Test Rate Limits

```bash
# Test rate limiting locally
for i in {1..15}; do
  curl -X POST https://your-project.supabase.co/functions/v1/your-function \
    -H "Authorization: Bearer YOUR_ANON_KEY" \
    -H "Content-Type: application/json" \
    -d '{"test": true}'
  echo " - Request $i"
  sleep 1
done
```

## Migration Checklist

When adding rate limiting to an existing function:

1. ✅ Determine the appropriate tier based on operation cost
2. ✅ Import `rateLimiters` and `withRateLimit` from `_shared/rateLimiter.ts`
3. ✅ Import `corsHeaders` from `_shared/cors.ts`
4. ✅ Wrap your handler with `withRateLimit(handler, rateLimiters.TIER, corsHeaders)`
5. ✅ Add a comment explaining the tier choice
6. ✅ Test the rate limit works correctly
7. ✅ Monitor edge function logs for rate limit hits
8. ✅ Adjust tier if needed based on real usage

## Troubleshooting

### Rate Limits Too Strict

**Symptoms:** Legitimate users hitting rate limits frequently

**Solutions:**
- Increase to next tier up (strict → moderate → standard → lenient)
- Consider per-user rate limiting instead of per-IP
- Check if the operation can be optimized to reduce frequency

### Rate Limits Too Lenient

**Symptoms:** Abuse patterns, high costs, slow performance

**Solutions:**
- Decrease to next tier down
- Add additional validation before expensive operations
- Consider implementing captcha for public endpoints

### Per-User Rate Limiting Not Working

**Check:**
- Is the Authorization header being sent?
- Is the JWT valid and parsable?
- Are logs showing IP-based limits instead of user-based?

## Examples from Production

### Example 1: Upload Function (STRICT)

```typescript
// upload-image function needs strict limiting because:
// - Makes external CloudFlare API calls ($$)
// - Processes large file uploads
// - High abuse potential
serve(withRateLimit(async (req) => {
  // Upload logic here
}, rateLimiters.strict, getCorsHeaders(allowedOrigin)));
```

### Example 2: Moderation Function (MODERATE)

```typescript
// process-selective-approval needs moderate limiting because:
// - Modifies database records
// - Triggers notifications
// - Used by moderators (need reasonable throughput)
serve(withRateLimit(handler, rateLimiters.moderate, corsHeaders));
```

### Example 3: Validation Function (LENIENT)

```typescript
// validate-email-backend can be lenient because:
// - Lightweight operation (just validation)
// - No database writes
// - Users may need to retry multiple times
serve(withRateLimit(async (req) => {
  // Validation logic here
}, rateLimiters.lenient, corsHeaders));
```

## Future Enhancements

Potential improvements to consider:

1. **Dynamic Rate Limits**: Adjust limits based on user role/tier
2. **Distributed Rate Limiting**: Use Redis for multi-region support
3. **Rate Limit Analytics**: Track and visualize rate limit metrics
4. **Custom Error Messages**: Provide context-specific retry guidance
5. **Whitelist Support**: Bypass limits for trusted IPs/users
