Overview
InsForge Functions have built-in access to the InsForge SDK without requiring any imports. The SDK is pre-injected into the function runtime and available globally ascreateClient
.
Quick Start
Copy
module.exports = async function(request) {
// Extract user token from Authorization header
const authHeader = request.headers.get('Authorization');
const userToken = authHeader ? authHeader.replace('Bearer ', '') : null;
// Create SDK client with user's token
// This is required to access user-specific data
const client = createClient({
baseUrl: Deno.env.get('BACKEND_INTERNAL_URL') || 'http://insforge:7130',
edgeFunctionToken: userToken // Pass user's token for authentication
});
// Now you can access user data
const { data: userData } = await client.auth.getCurrentUser();
if (userData?.user?.id) {
// User is authenticated, can perform user-specific operations
return new Response(
JSON.stringify({ userId: userData.user.id }),
{ headers: { 'Content-Type': 'application/json' } }
);
}
return new Response('Unauthorized', { status: 401 });
}
Client Configuration
Copy
const client = createClient({
baseUrl: Deno.env.get('BACKEND_INTERNAL_URL') || 'http://insforge:7130',
edgeFunctionToken: userToken // Required for authenticated operations
});
Parameter | Type | Description |
---|---|---|
baseUrl | string | Backend API URL (use BACKEND_INTERNAL_URL env var or http://insforge:7130 ) |
edgeFunctionToken | string | JWT token - either user’s token from Authorization header or ACCESS_API_KEY from env |
Common Examples
Protected API Endpoint
Create an authenticated endpoint that returns user-specific data:Copy
module.exports = async function(request) {
// Get user token from request
const authHeader = request.headers.get('Authorization');
if (!authHeader?.startsWith('Bearer ')) {
return new Response('Unauthorized', { status: 401 });
}
// Initialize SDK with user's token
const client = createClient({
baseUrl: 'http://insforge:7130',
edgeFunctionToken: authHeader.replace('Bearer ', '')
});
// Verify user is authenticated
const { data: userData } = await client.auth.getCurrentUser();
if (!userData?.user?.id) {
return new Response('Invalid token', { status: 401 });
}
// Return user-specific data
const { data: userPosts } = await client.database
.from('posts')
.select('*')
.eq('user_id', userData.user.id)
.order('created_at', { ascending: false });
return new Response(JSON.stringify(userPosts), {
headers: { 'Content-Type': 'application/json' }
});
}
Webhook Handler
Process incoming webhooks from external services:Copy
module.exports = async function(request) {
// Verify webhook signature
const signature = request.headers.get('x-webhook-signature');
const secret = Deno.env.get('WEBHOOK_SECRET');
if (!signature || signature !== secret) {
return new Response('Invalid signature', { status: 401 });
}
// Parse webhook payload
const payload = await request.json();
// For webhooks, use the ACCESS_API_KEY from environment
const accessToken = Deno.env.get('ACCESS_API_KEY');
const client = createClient({
baseUrl: Deno.env.get('BACKEND_INTERNAL_URL') || 'http://insforge:7130',
edgeFunctionToken: accessToken // System access token for backend operations
});
// Process webhook event
switch (payload.event) {
case 'payment.success':
await client.database
.from('orders')
.update({ status: 'paid' })
.eq('id', payload.order_id);
break;
case 'user.created':
await client.database
.from('users')
.insert([{
external_id: payload.user_id,
email: payload.email
}]);
break;
}
return new Response('OK', { status: 200 });
}
File Upload Handler
Handle file uploads with validation:Copy
module.exports = async function(request) {
// Check authentication
const token = request.headers.get('Authorization')?.replace('Bearer ', '');
if (!token) {
return new Response('Unauthorized', { status: 401 });
}
const client = createClient({
baseUrl: 'http://insforge:7130',
edgeFunctionToken: token
});
// Parse form data
const formData = await request.formData();
const file = formData.get('file');
// Validate file
if (!file || file.size > 5 * 1024 * 1024) {
return new Response('Invalid file or too large (max 5MB)', { status: 400 });
}
// Upload to storage
const { data, error } = await client.storage
.from('user-uploads')
.uploadAuto(file);
if (error) {
return new Response('Upload failed', { status: 500 });
}
return new Response(
JSON.stringify({ url: data.url }),
{ headers: { 'Content-Type': 'application/json' } }
);
}
API Gateway
Route different actions to different handlers:Copy
module.exports = async function(request) {
const url = new URL(request.url);
const action = url.searchParams.get('action');
const client = createClient({
baseUrl: 'http://insforge:7130'
});
switch (action) {
case 'search':
const query = url.searchParams.get('q');
const { data } = await client.database
.from('products')
.select('*')
.ilike('name', `%${query}%`)
.limit(10);
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' }
});
case 'stats':
const { count } = await client.database
.from('orders')
.select('*', { count: 'exact', head: true });
return new Response(JSON.stringify({ total_orders: count }), {
headers: { 'Content-Type': 'application/json' }
});
default:
return new Response('Invalid action', { status: 400 });
}
}
Environment Variables (Secrets)
Access secrets usingDeno.env.get()
:
Copy
module.exports = async function(request) {
// Access secret values
const apiKey = Deno.env.get('STRIPE_API_KEY');
const dbUrl = Deno.env.get('DATABASE_URL');
const webhookSecret = Deno.env.get('WEBHOOK_SECRET');
if (!apiKey) {
return new Response('API key not configured', { status: 500 });
}
// Use secrets in your function
const response = await fetch('https://api.stripe.com/v1/charges', {
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
}
Request Handling
Request Object
Copy
module.exports = async function(request) {
// Method: GET, POST, PUT, DELETE, etc.
const method = request.method;
// URL and query parameters
const url = new URL(request.url);
const page = url.searchParams.get('page');
const search = url.searchParams.get('q');
// Headers
const contentType = request.headers.get('content-type');
const authorization = request.headers.get('authorization');
// Body parsing
const json = await request.json(); // For JSON
const text = await request.text(); // For plain text
const formData = await request.formData(); // For multipart
}
Response Object
Copy
// JSON response
return new Response(
JSON.stringify({ data: result }),
{
status: 200,
headers: { 'Content-Type': 'application/json' }
}
);
// Text response
return new Response('Plain text', {
headers: { 'Content-Type': 'text/plain' }
});
// HTML response
return new Response('<html>...</html>', {
headers: { 'Content-Type': 'text/html' }
});
// No content
return new Response(null, { status: 204 });
// Error response
return new Response(
JSON.stringify({ error: 'Not found' }),
{
status: 404,
headers: { 'Content-Type': 'application/json' }
}
);
CORS Headers
Copy
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
};
// Handle preflight
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: corsHeaders
});
}
// Add CORS to response
return new Response(JSON.stringify(data), {
headers: {
...corsHeaders,
'Content-Type': 'application/json'
}
});
Error Handling
Copy
module.exports = async function(request) {
try {
const client = createClient({
baseUrl: 'http://insforge:7130',
edgeFunctionToken: request.headers.get('Authorization')?.replace('Bearer ', '')
});
const { data, error } = await client.database
.from('posts')
.select('*');
// Check SDK errors
if (error) {
console.error('Database error:', error);
return new Response(
JSON.stringify({ error: error.message }),
{ status: 500, headers: { 'Content-Type': 'application/json' } }
);
}
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
// Handle unexpected errors
console.error('Function error:', error);
return new Response(
JSON.stringify({
error: 'Internal server error',
message: error.message
}),
{
status: 500,
headers: { 'Content-Type': 'application/json' }
}
);
}
}
Complete Example
Copy
module.exports = async function(request) {
// Setup CORS
const corsHeaders = {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
};
// Handle OPTIONS
if (request.method === 'OPTIONS') {
return new Response(null, { status: 204, headers: corsHeaders });
}
// Get auth token
const authHeader = request.headers.get('Authorization');
const token = authHeader?.replace('Bearer ', '');
if (!token) {
return new Response('Unauthorized', {
status: 401,
headers: corsHeaders
});
}
try {
// Initialize SDK
const client = createClient({
baseUrl: 'http://insforge:7130',
edgeFunctionToken: token
});
// Get current user
const { data: userData, error: userError } = await client.auth.getCurrentUser();
if (!userData?.user?.id) {
return new Response('Invalid token', {
status: 401,
headers: corsHeaders
});
}
// Parse request
const url = new URL(request.url);
const action = url.searchParams.get('action');
switch (action) {
case 'list': {
// List user's posts
const { data, error } = await client.database
.from('posts')
.select('*, users!inner(nickname)')
.eq('user_id', userData.user.id)
.order('created_at', { ascending: false });
if (error) throw error;
return new Response(JSON.stringify(data), {
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
case 'create': {
// Create new post
const body = await request.json();
const { data, error } = await client.database
.from('posts')
.insert([{
user_id: userData.user.id,
title: body.title,
content: body.content
}])
.select()
.single();
if (error) throw error;
return new Response(JSON.stringify(data), {
status: 201,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
});
}
default:
return new Response('Invalid action', {
status: 400,
headers: corsHeaders
});
}
} catch (error) {
console.error('Function error:', error);
return new Response(
JSON.stringify({ error: error.message }),
{
status: 500,
headers: { ...corsHeaders, 'Content-Type': 'application/json' }
}
);
}
}
TypeScript Support
While TypeScript is supported, types are not checked at runtime:Copy
interface RequestBody {
title: string;
content: string;
}
module.exports = async function(request: Request): Promise<Response> {
const body = await request.json() as RequestBody;
// TypeScript syntax works but no compile-time checking
const response = {
id: crypto.randomUUID(),
title: body.title,
content: body.content,
created: new Date().toISOString()
};
return new Response(
JSON.stringify(response),
{ headers: { 'Content-Type': 'application/json' } }
);
}
Performance Tips
- Reuse client instances - Create client once per function execution
- Select only needed columns - Use
.select('id, title')
instead of*
- Use proper indexes - Ensure database indexes for filtered columns
- Batch operations - Insert/update multiple records in one call
- Handle errors early - Validate before expensive operations
- Use caching headers - Add
Cache-Control
for static responses
Debugging
Copy
// Use console.log for debugging
console.log('Function started');
console.log('Request:', {
method: request.method,
url: request.url,
headers: Object.fromEntries(request.headers)
});
// Logs appear in Deno container
// View with: docker logs insforge-deno -f