Skip to content

Instantly share code, notes, and snippets.

@bmorrisondev
Created January 21, 2025 22:41
Show Gist options
  • Select an option

  • Save bmorrisondev/a8c7910b8d2e02e777146223e6366930 to your computer and use it in GitHub Desktop.

Select an option

Save bmorrisondev/a8c7910b8d2e02e777146223e6366930 to your computer and use it in GitHub Desktop.
// Follow this setup guide to integrate the Deno language server with your editor:
// https://deno.land/manual/getting_started/setup_your_environment
// This enables autocomplete, go to definition, etc.
// Setup type definitions for built-in Supabase Runtime APIs
import "jsr:@supabase/functions-js/edge-runtime.d.ts"
import { createClient } from 'https://esm.sh/@supabase/[email protected]'
console.log("Hello from Functions!")
interface WebhookEvent {
data: {
id: string;
first_name?: string;
last_name?: string;
name?: string;
image_url?: string;
email_addresses?: Array<{
email_address: string;
verification: {
status: string;
};
}>;
primary_email_address_id?: string;
public_metadata?: Record<string, unknown>;
unsafe_metadata?: Record<string, unknown>;
created_at: number;
updated_at: number;
// Organization data
organization?: {
id: string;
name: string;
created_at: number;
updated_at: number;
};
// Public user data for org membership
public_user_data?: {
user_id: string;
first_name?: string;
last_name?: string;
image_url?: string;
email_address?: string;
};
};
object: 'event';
type: string;
}
Deno.serve(async (req) => {
// Verify webhook signature
const webhookSecret = Deno.env.get('CLERK_WEBHOOK_SECRET')
if (!webhookSecret) {
return new Response('Webhook secret not configured', { status: 500 })
}
// Get the headers
const svix_id = req.headers.get('svix-id')
const svix_timestamp = req.headers.get('svix-timestamp')
const svix_signature = req.headers.get('svix-signature')
// If there are no headers, error out
if (!svix_id || !svix_timestamp || !svix_signature) {
return new Response('Error occured -- no svix headers', { status: 400 })
}
// Get the body
const payload = await req.json()
const body = JSON.stringify(payload)
// Create supabase client
const supabaseUrl = Deno.env.get('SUPABASE_URL')
const supabaseServiceKey = Deno.env.get('SUPABASE_SERVICE_ROLE_KEY')
if (!supabaseUrl || !supabaseServiceKey) {
return new Response('Supabase credentials not configured', { status: 500 })
}
const supabase = createClient(supabaseUrl, supabaseServiceKey)
// Handle the webhook
const event = payload as WebhookEvent
switch (event.type) {
case 'user.created': {
// Handle user creation
const { data: user, error } = await supabase
.from('users')
.insert([
{
id: event.data.id,
first_name: event.data.first_name,
last_name: event.data.last_name,
avatar_url: event.data.image_url,
created_at: new Date(event.data.created_at).toISOString(),
updated_at: new Date(event.data.updated_at).toISOString(),
},
])
.select()
.single()
if (error) {
console.error('Error creating user:', error)
return new Response(JSON.stringify({ error: error.message }), { status: 500 })
}
return new Response(JSON.stringify({ user }), { status: 200 })
}
case 'user.updated': {
// Handle user update
const { data: user, error } = await supabase
.from('users')
.update({
first_name: event.data.first_name,
last_name: event.data.last_name,
avatar_url: event.data.image_url,
updated_at: new Date(event.data.updated_at).toISOString(),
})
.eq('id', event.data.id)
.select()
.single()
if (error) {
console.error('Error updating user:', error)
return new Response(JSON.stringify({ error: error.message }), { status: 500 })
}
return new Response(JSON.stringify({ user }), { status: 200 })
}
case 'organization.created': {
// Handle user update
const { data, error } = await supabase
.from('organizations')
.create({
id: event.data.id,
name: event.data.name,
updated_at: new Date(event.data.updated_at).toISOString(),
})
.select()
.single()
if (error) {
console.error('Error updating owner:', error)
return new Response(JSON.stringify({ error: error.message }), { status: 500 })
}
return new Response(JSON.stringify({ data }), { status: 200 })
}
case 'organization.updated': {
const { data, error } = await supabase
.from('owners')
.update({
name: event.data.name,
updated_at: new Date(event.data.updated_at).toISOString(),
})
.eq('id', event.data.id)
.select()
.single()
if (error) {
console.error('Error updating owner:', error)
return new Response(JSON.stringify({ error: error.message }), { status: 500 })
}
return new Response(JSON.stringify({ data }), { status: 200 })
}
case 'organizationMembership.created': {
const { data, error } = await supabase
.from('members')
.insert([
{
id: event.data.id,
user_id: event.data.public_user_data?.user_id,
organization_id: event.data.organization?.id,
created_at: new Date(event.data.created_at).toISOString(),
updated_at: new Date(event.data.updated_at).toISOString(),
},
])
.select()
.single()
if (error) {
console.error('Error updating member:', error)
return new Response(JSON.stringify({ error: error.message }), { status: 500 })
}
return new Response(JSON.stringify({ data }), { status: 200 })
}
case 'organizationMembership.updated': {
const { data, error } = await supabase
.from('members')
.update({
user_id: event.data.public_user_data?.user_id,
organization_id: event.data.organization?.id,
updated_at: new Date(event.data.updated_at).toISOString(),
})
.eq('id', event.data.id)
.select()
.single()
if (error) {
console.error('Error updating member:', error)
return new Response(JSON.stringify({ error: error.message }), { status: 500 })
}
return new Response(JSON.stringify({ data }), { status: 200 })
}
default: {
// Unhandled event type
console.log('Unhandled event type:', JSON.stringify(event, null, 2))
return new Response(JSON.stringify({ success: true }), { status: 200 })
}
}
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment