Source:
ocean/docs/patterns/edge-functions.md| ✏️ Edit on GitHub
Edge Functions Pattern Reference
Quick Reference: All Edge Functions use standardized wrapper patterns for authentication, CORS, and error handling.
Wrapper Types
1. createEdgeFunction
Use for: Standard authenticated endpoints
import { createEdgeFunction } from '../_shared/function-wrapper.ts'
export default createEdgeFunction(async (req, context) => {
const { user, supabaseClient, logger } = context
// user.id is guaranteed to exist
// supabaseClient has RLS enabled for this user
return new Response(JSON.stringify({ userId: user.id }))
})
Features:
- ✅ JWT authentication required
- ✅ User context available
- ✅ Supabase client with RLS
- ✅ Automatic CORS
- ✅ Error handling with Sentry
2. createPublicFunction
Use for: Public endpoints (no authentication)
import { createPublicFunction } from '../_shared/function-wrapper.ts'
export default createPublicFunction(async (req, context) => {
const { supabaseClient, logger } = context
// No user context - public access
// supabaseClient uses service role (no RLS)
return new Response('Public endpoint')
})
Features:
- ❌ No authentication required
- ❌ No user context
- ✅ Service role Supabase client
- ✅ Automatic CORS
- ✅ Error handling with Sentry
3. createStripeFunction
Use for: Stripe API interactions (public or authenticated)
// Public Stripe function (like setup intents)
import { createStripeFunction } from '../_shared/function-wrapper.ts'
export default createStripeFunction(
async (req, context) => {
const { stripe, logger } = context
// Create Stripe resources
const paymentIntent = await stripe.paymentIntents.create({
amount: 2000,
currency: 'usd',
})
return Response.json({ client_secret: paymentIntent.client_secret })
},
{ requireAuth: false }
)
// Authenticated Stripe function (like subscriptions)
export default createStripeFunction(async (req, context) => {
const { user, stripe, supabaseClient } = context
// Both user context AND Stripe SDK available
return Response.json({ success: true })
})
Features:
- ✅ Stripe SDK initialized
- ✅ Configurable authentication (
requireAuth: true/false) - ✅ User context when authenticated
- ✅ Automatic CORS
- ✅ Error handling with Sentry
4. createWebhookFunction
Use for: External webhook endpoints
import { createWebhookFunction } from '../_shared/function-wrapper.ts'
export default createWebhookFunction(
async (req, context) => {
const { rawBody, logger } = context
// Signature validation handled automatically
// Raw body available for verification
const event = JSON.parse(rawBody)
return new Response('OK')
},
{
expectedHeader: 'stripe-signature',
webhookSecret: Deno.env.get('STRIPE_WEBHOOK_SECRET')!,
}
)
Features:
- ✅ Signature validation
- ✅ Raw body access
- ✅ Configurable webhook validation
- ❌ No user context
- ✅ Automatic CORS
- ✅ Error handling with Sentry
5. createGraphQLFunction
Use for: GraphQL Yoga servers
import { createGraphQLFunction } from '../_shared/function-wrapper.ts'
import { createYoga } from 'graphql-yoga'
const yoga = createYoga({
schema: typeDefs,
resolvers: resolvers,
})
export default createGraphQLFunction((context) => ({
yoga,
allowedOrigins: ['http://localhost:3000', 'https://ocean.goldfish.io'],
}))
Features:
- ✅ GraphQL Yoga integration
- ✅ CORS configuration
- ✅ Request context passing
- ✅ Authentication context available in resolvers
Context Objects
Authenticated Context
interface AuthenticatedContext {
user: {
id: string
email?: string
}
supabaseClient: SupabaseClient // RLS enabled
logger: Logger
}
Public Context
interface PublicContext {
supabaseClient: SupabaseClient // Service role
logger: Logger
}
Stripe Context
interface StripeContext {
stripe: Stripe
user?: { id: string; email?: string } // If requireAuth: true
supabaseClient: SupabaseClient
logger: Logger
}
Webhook Context
interface WebhookContext {
rawBody: string
logger: Logger
}
Common Patterns
Database Queries
export default createEdgeFunction(async (req, context) => {
const { user, supabaseClient } = context
// RLS automatically filters for user's data
const { data } = await supabaseClient.from('organizations').select('*').eq('owner_id', user.id)
return Response.json(data)
})
Error Handling
export default createEdgeFunction(async (req, context) => {
const { logger } = context
try {
// Business logic
} catch (error) {
// Automatic Sentry logging via wrapper
logger.error('Operation failed', { error })
throw error // Wrapper handles HTTP error response
}
})
Request Parsing
export default createEdgeFunction(async (req, context) => {
if (req.method !== 'POST') {
return new Response('Method not allowed', { status: 405 })
}
const body = await req.json()
// Validate body...
return Response.json({ success: true })
})
File Organization
supabase/functions/
├── _shared/
│ ├── function-wrapper.ts # All wrapper functions
│ ├── auth.ts # Auth utilities
│ └── stripe-client.ts # Stripe initialization
├── graphql-v2/
│ ├── index.ts # Uses createGraphQLFunction
│ └── setup.ts # GraphQL setup logic
├── stripe-subscription/
│ └── index.ts # Uses createStripeFunction
├── handle-stripe-webhook/
│ └── index.ts # Uses createWebhookFunction
└── provision-tenant-resources/
└── index.ts # Uses createEdgeFunction
Debugging
Test Locally
supabase functions serve stripe-subscription --no-verify-jwt
curl -X POST http://localhost:54321/functions/v1/stripe-subscription
View Logs
supabase functions logs stripe-subscription
Common Issues
CORS Errors: All wrappers handle CORS automatically. If you see CORS errors, check wrapper usage.
Auth Errors:
createEdgeFunctionrequires JWT token inAuthorization: Bearer <token>headercreatePublicFunctionnever requires authcreateStripeFunctionrequires auth ifrequireAuth: true
Context Missing: Check wrapper type - each provides different context objects.