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

660 lines
14 KiB
Markdown

# Webhook Testing Guide
Complete guide for testing the Clerk webhook integration that syncs users to your database.
## Table of Contents
1. [Prerequisites](#prerequisites)
2. [Local Development Testing](#local-development-testing)
3. [Production Testing](#production-testing)
4. [Test Scenarios](#test-scenarios)
5. [Verification Steps](#verification-steps)
6. [Common Issues](#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
```bash
# 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
### Option 1: Using Clerk CLI (Recommended)
The Clerk CLI forwards webhook events from Clerk to your local server.
#### Step 1: Install Clerk CLI
```bash
npm install -g @clerk/clerk-cli
```
#### Step 2: Login to Clerk
```bash
clerk login
```
Follow the prompts to authenticate with your Clerk account.
#### Step 3: Start Your Dev Server
```bash
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**:
```bash
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`:
```env
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](https://www.svix.com/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:
```bash
# 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](https://dashboard.clerk.com)
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:
```env
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:
```sql
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:**
```sql
-- 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:**
```sql
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:**
```sql
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:**
```sql
-- 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:**
```sql
-- 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:**
```bash
# 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:**
```sql
-- 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:**
```bash
# 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:**
```bash
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:**
```typescript
// 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:**
```bash
cd packages/database
npm run db:studio
# Verify you can see the database
```
2. **Check for unique constraint violations:**
```sql
-- Check if user with email already exists
SELECT * FROM users WHERE email = 'test@example.com';
```
3. **Verify schema migration ran:**
```bash
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:**
```json
{
"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:**
```bash
# 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:**
```typescript
// Add indexes
// Run migrations separately
// Use transactions for bulk operations
```
2. **Add timeout handling:**
```typescript
// Process webhook asynchronously
// Return 200 immediately
// Handle sync in background
```
3. **Check database connection pool:**
```typescript
// Ensure connection pool is sized correctly
// Close connections properly
```
---
## Automated Testing
### Unit Test Example
```typescript
// 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
- **Clerk Webhooks Docs:** https://clerk.com/docs/integrations/webhooks
- **Svix Documentation:** https://docs.svix.com
- **Project Documentation:** See `CLERK_WEBHOOK_SETUP.md`
- **Troubleshooting:** See `TROUBLESHOOTING.md`
---
**Last Updated:** 2024
**Webhook Handler:** `apps/admin/src/app/api/webhooks/route.ts`