fitaiProto/docs/WEBHOOK_TESTING_GUIDE.md
2025-11-19 05:12:19 +01:00

14 KiB

Webhook Testing Guide

Complete guide for testing the Clerk webhook integration that syncs users to your database.

Table of Contents

  1. Prerequisites
  2. Local Development Testing
  3. Production Testing
  4. Test Scenarios
  5. Verification Steps
  6. Common Issues

Prerequisites

Before testing webhooks, ensure you have:

  • Clerk account and application set up
  • Admin app running locally or deployed
  • Database schema updated (password field optional)
  • CLERK_WEBHOOK_SECRET environment variable set
  • svix package installed in admin app

Quick Setup Check

# 1. Check environment variables
cat apps/admin/.env.local | grep CLERK_WEBHOOK_SECRET

# 2. Verify svix is installed
cd apps/admin
npm list svix

# 3. Check database schema
cd ../../packages/database
npm run db:push

Local Development Testing

The Clerk CLI forwards webhook events from Clerk to your local server.

Step 1: Install Clerk CLI

npm install -g @clerk/clerk-cli

Step 2: Login to Clerk

clerk login

Follow the prompts to authenticate with your Clerk account.

Step 3: Start Your Dev Server

cd apps/admin
npm run dev

Your server should be running at http://localhost:3000

Step 4: Start Webhook Forwarding

In a new terminal window:

clerk listen --forward-url http://localhost:3000/api/webhooks

You'll see output like:

✓ Webhook forwarding is now active!
✓ Webhook secret: whsec_xxxxxxxxxxxxxx

Forwarding webhooks to: http://localhost:3000/api/webhooks

Step 5: Copy the Webhook Secret

Copy the webhook secret from the output and add it to your .env.local:

CLERK_WEBHOOK_SECRET=whsec_xxxxxxxxxxxxxx

Restart your dev server after adding the secret.

Step 6: Test with a Real Sign-Up

  1. Go to your app (mobile or admin)
  2. Sign up a new user
  3. Watch both terminals:
    • Clerk CLI terminal shows the webhook being forwarded
    • Dev server terminal shows the processing logs

Expected output:

Received webhook with ID user_abc123 and type user.created
✅ User user_abc123 created in database

Option 2: Using Webhook Testing Tools

Using Svix Play (Web UI)

  1. Go to Svix Play
  2. Enter your webhook URL: http://localhost:3000/api/webhooks
  3. Select "Clerk" as the provider
  4. Choose event type: user.created
  5. Modify the payload if needed
  6. Click "Send"

Note: You'll need to expose your local server (use ngrok) for this to work.

Using ngrok for Local Testing

If you need a public URL:

# Install ngrok
npm install -g ngrok

# Start ngrok
ngrok http 3000

Use the ngrok URL (e.g., https://abc123.ngrok.io/api/webhooks) in Clerk Dashboard or Svix Play.


Production Testing

Step 1: Configure Webhook Endpoint in Clerk Dashboard

  1. Go to Clerk Dashboard
  2. Select your application
  3. Navigate to Webhooks in sidebar
  4. Click Add Endpoint

Configure Endpoint:

  • Endpoint URL: https://yourdomain.com/api/webhooks
  • Events to subscribe to:
    • user.created
    • user.updated
    • user.deleted

Copy Signing Secret:

After creating the endpoint, copy the Signing Secret and add it to your production environment variables:

CLERK_WEBHOOK_SECRET=whsec_prod_xxxxxxxxxxxxxx

Step 2: Send Test Event

In Clerk Dashboard webhook settings:

  1. Click on your endpoint
  2. Click Send Test Event
  3. Select user.created
  4. Click Send

Step 3: Verify in Dashboard

Check the webhook attempt logs:

  • Status: Should be 200 OK
  • Response: "Webhook processed successfully"
  • Response Time: < 2 seconds typically

Step 4: Verify in Database

Query your database to verify the test user was created:

SELECT * FROM users ORDER BY created_at DESC LIMIT 1;

Test Scenarios

Scenario 1: User Creation

Objective: Verify new users are synced to database

Steps:

  1. Create a new user via sign-up flow
  2. Check database for new user record
  3. Verify all fields are populated correctly

Expected Result:

-- User should exist in database
SELECT * FROM users WHERE id = 'user_abc123';

-- Result:
-- id: user_abc123
-- email: test@example.com
-- firstName: John
-- lastName: Doe
-- role: client (default)
-- password: NULL or empty
-- createdAt: [timestamp]
-- updatedAt: [timestamp]

Success Criteria:

  • User record exists
  • Email matches Clerk
  • Name fields populated
  • Default role is 'client'
  • Timestamps are set

Scenario 2: User Update

Objective: Verify user updates sync to database

Steps:

  1. Update user profile in Clerk (name, email, or role)
  2. Check database for updated values
  3. Verify updatedAt timestamp changed

Update Name via Clerk Dashboard:

  1. Go to Users → Select user
  2. Update First Name to "Jane"
  3. Click Save

Expected Result:

SELECT * FROM users WHERE id = 'user_abc123';

-- firstName should now be: Jane
-- updatedAt should be newer than createdAt

Success Criteria:

  • User fields updated
  • updatedAt timestamp changed
  • createdAt timestamp unchanged

Scenario 3: Role Assignment

Objective: Verify role metadata syncs correctly

Steps:

  1. Set user role via Clerk Dashboard:
    • Users → Select user
    • Public Metadata: {"role": "trainer"}
    • Save
  2. Check database for role update

Expected Result:

SELECT id, email, role FROM users WHERE id = 'user_abc123';

-- role should be: trainer

Success Criteria:

  • Role updated to 'trainer'
  • Role persisted in database
  • Webhook processed successfully

Scenario 4: User Deletion

Objective: Verify user deletion cascades correctly

Steps:

  1. Note user ID: user_abc123
  2. Delete user from Clerk Dashboard
  3. Check database - user should be deleted

Expected Result:

-- User should not exist
SELECT * FROM users WHERE id = 'user_abc123';
-- Returns: 0 rows

-- Related records should also be deleted (cascade)
SELECT * FROM notifications WHERE user_id = 'user_abc123';
-- Returns: 0 rows

Success Criteria:

  • User record deleted
  • Related records deleted (notifications, etc.)
  • No orphaned data

Scenario 5: Bulk User Creation

Objective: Test webhook handling under load

Steps:

  1. Import multiple users via Clerk Dashboard or API
  2. Verify all users created in database
  3. Check for any failed webhooks

Expected Result:

-- All users should exist
SELECT COUNT(*) FROM users WHERE created_at > NOW() - INTERVAL 5 MINUTE;
-- Should match number of users created

Success Criteria:

  • All users synced
  • No duplicate entries
  • All webhook attempts successful

Verification Steps

1. Check Webhook Logs (Clerk Dashboard)

Location: Clerk Dashboard → Webhooks → Your Endpoint

What to Check:

  • Status Code: Should be 200
  • Response Time: Typically < 2 seconds
  • Response Body: "Webhook processed successfully"
  • Attempts: Should be 1 (no retries needed)

Red Flags:

  • Status 400: Verification failed (wrong secret)
  • Status 500: Server error (check logs)
  • Multiple attempts: Server issues or slow response

2. Check Server Logs

Look for these log messages:

# Success logs
✅ User user_abc123 created in database
✅ User user_abc123 updated in database
✅ User user_abc123 deleted from database

# Info logs
Received webhook with ID user_abc123 and type user.created

# Error logs (should not appear)
❌ Error verifying webhook: [details]
❌ Error processing webhook: [details]
❌ No primary email found for user: [id]

3. Verify Database State

Query Templates:

-- Check recent users
SELECT * FROM users ORDER BY created_at DESC LIMIT 10;

-- Check user count by role
SELECT role, COUNT(*) as count FROM users GROUP BY role;

-- Check for users without required fields
SELECT * FROM users WHERE email IS NULL OR firstName = '' OR lastName = '';

-- Check for orphaned records (shouldn't exist after cascade delete)
SELECT n.* FROM notifications n 
LEFT JOIN users u ON n.user_id = u.id 
WHERE u.id IS NULL;

4. Test Role Assignment API

Using curl:

# Get your auth token from Clerk
TOKEN="your_clerk_session_token"

# Set user role
curl -X POST http://localhost:3000/api/admin/set-role \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKEN" \
  -d '{
    "targetUserId": "user_abc123",
    "role": "trainer"
  }'

# Expected response:
# {
#   "success": true,
#   "message": "User role updated to trainer",
#   "user": { ... }
# }

Common Issues

Issue 1: Webhook Returns 400 - Verification Failed

Symptoms:

Error verifying webhook: [Svix error details]
Status: 400

Causes:

  • Wrong webhook secret
  • Missing Svix headers
  • Replay attack (old timestamp)

Solutions:

  1. Verify webhook secret:

    echo $CLERK_WEBHOOK_SECRET
    # Should start with whsec_
    
  2. Regenerate secret:

    • Go to Clerk Dashboard → Webhooks
    • Click your endpoint → Rotate Secret
    • Update .env.local with new secret
    • Restart server
  3. Check headers:

    // Add debug logging to webhook handler
    console.log('Headers:', {
      svix_id: headerPayload.get('svix-id'),
      svix_timestamp: headerPayload.get('svix-timestamp'),
      svix_signature: headerPayload.get('svix-signature'),
    });
    

Issue 2: User Not Created in Database

Symptoms:

  • Webhook returns 200
  • Server logs show success
  • User not in database

Solutions:

  1. Check database connection:

    cd packages/database
    npm run db:studio
    # Verify you can see the database
    
  2. Check for unique constraint violations:

    -- Check if user with email already exists
    SELECT * FROM users WHERE email = 'test@example.com';
    
  3. Verify schema migration ran:

    cd packages/database
    npm run db:push
    
  4. Check server logs for SQL errors:

    Error processing webhook: UNIQUE constraint failed: users.email
    

Issue 3: Role Not Updating

Symptoms:

  • Webhook processed successfully
  • Role in database doesn't change

Solutions:

  1. Verify metadata format in Clerk:

    {
      "role": "admin"
    }
    

    (Should be in Public Metadata, not Private or Unsafe)

  2. Check role value:

    • Must be exactly: admin, trainer, or client
    • Case-sensitive
    • No extra spaces
  3. Force webhook retry:

    • Update any field on the user
    • This triggers user.updated webhook

Issue 4: Webhook Not Received

Symptoms:

  • Event in Clerk
  • No webhook attempt logged
  • Server never hit

Solutions:

  1. Verify endpoint URL in Clerk Dashboard:

    Production: https://yourdomain.com/api/webhooks
    Local (with ngrok): https://abc123.ngrok.io/api/webhooks
    
  2. Check subscribed events:

    • Ensure user.created, user.updated, user.deleted are checked
  3. Verify server is accessible:

    # Test endpoint manually
    curl -X POST https://yourdomain.com/api/webhooks \
      -H "Content-Type: application/json" \
      -d '{"test": true}'
    
    # Should return 400 (missing headers), not 404
    
  4. Check firewall/security rules:

    • Ensure Clerk IPs not blocked
    • HTTPS must be valid (not self-signed cert)

Issue 5: Slow Webhook Processing

Symptoms:

  • Webhook takes > 5 seconds
  • Clerk retries multiple times
  • Timeouts

Solutions:

  1. Optimize database queries:

    // Add indexes
    // Run migrations separately
    // Use transactions for bulk operations
    
  2. Add timeout handling:

    // Process webhook asynchronously
    // Return 200 immediately
    // Handle sync in background
    
  3. Check database connection pool:

    // Ensure connection pool is sized correctly
    // Close connections properly
    

Automated Testing

Unit Test Example

// apps/admin/__tests__/webhook.test.ts
import { POST } from '@/app/api/webhooks/route';

describe('Webhook Handler', () => {
  it('should create user on user.created event', async () => {
    const mockRequest = new Request('http://localhost/api/webhooks', {
      method: 'POST',
      headers: {
        'svix-id': 'msg_test',
        'svix-timestamp': Date.now().toString(),
        'svix-signature': 'test_signature',
      },
      body: JSON.stringify({
        type: 'user.created',
        data: {
          id: 'user_test123',
          email_addresses: [
            {
              id: 'email_test',
              email_address: 'test@example.com',
            },
          ],
          primary_email_address_id: 'email_test',
          first_name: 'Test',
          last_name: 'User',
          public_metadata: { role: 'client' },
        },
      }),
    });

    const response = await POST(mockRequest);
    expect(response.status).toBe(200);
  });
});

Monitoring Checklist

Use this checklist for ongoing webhook monitoring:

  • Check webhook success rate in Clerk Dashboard (should be > 99%)
  • Monitor webhook response times (should be < 2s)
  • Review failed webhook attempts weekly
  • Verify user count in Clerk matches database
  • Check for orphaned records monthly
  • Test role assignment flow quarterly
  • Review webhook logs for errors
  • Validate cascade deletes are working

Next Steps After Testing

Once webhooks are tested and working:

  1. Document for your team - Share this guide
  2. Set up monitoring - Use Clerk Dashboard or custom monitoring
  3. Plan for scale - Consider async processing for high volume
  4. Implement role UI - Build admin interface for role management
  5. Add audit logging - Track who changes what roles
  6. Test failure scenarios - What happens if database is down?

Support Resources


Last Updated: 2024 Webhook Handler: apps/admin/src/app/api/webhooks/route.ts