Cloud Functions
Deploy serverless edge functions on decentralized infrastructure with Alternate Futures Functions.
What are Cloud Functions?
Cloud Functions are serverless compute that runs on the edge, close to your users. They enable:
- API Endpoints - Create backend APIs without managing servers
- Dynamic Content - Generate personalized content at the edge
- Data Processing - Transform and process data on-demand
- Webhooks - Handle incoming webhook events
- Form Handling - Process form submissions
Function Runtime
Functions run in a secure JavaScript/TypeScript runtime with:
- Node.js-compatible APIs
- Access to Web APIs (fetch, Request, Response)
- Optional SGX (Software Guard Extensions) for enhanced security
- Automatic scaling based on demand
Creating a Function
# Create a new function
af functions create --name my-function
# Create and attach to a site
af functions create --name my-function --site-id <site-id>import { AlternateFuturesSdk } from '@alternatefutures/sdk/node';
const af = new AlternateFuturesSdk({
personalAccessToken: process.env.AF_TOKEN
});
// Create a function
const func = await af.functions().create({
name: 'my-function',
siteId: 'site-id', // optional
routes: {
'/api/*': 'index.js' // route patterns
}
});
console.log('Function created:', func.name);
console.log('Function ID:', func.id);Writing Function Code
Create an index.js file with your function logic:
// Simple API endpoint
export default async function handler(request) {
const url = new URL(request.url);
// Handle different routes
if (url.pathname === '/api/hello') {
return new Response(JSON.stringify({
message: 'Hello from Alternate Futures!'
}), {
headers: { 'Content-Type': 'application/json' }
});
}
// Default response
return new Response('Not Found', { status: 404 });
}Advanced Example
// Function with database and external API
export default async function handler(request) {
const url = new URL(request.url);
// Parse request body
if (request.method === 'POST') {
const data = await request.json();
// Call external API
const response = await fetch('https://api.example.com/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
const result = await response.json();
return new Response(JSON.stringify(result), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*'
}
});
}
return new Response('Method Not Allowed', { status: 405 });
}Deploying a Function
# Deploy function code
af functions deploy \
--function-id <function-id> \
--code ./dist
# Deploy with SGX security
af functions deploy \
--function-id <function-id> \
--code ./dist \
--sgx
# Deploy with assets
af functions deploy \
--function-id <function-id> \
--code ./dist \
--assets ./public// First, upload your code to IPFS
const codeUpload = await af.uploadProxy().uploadDirectory({
path: './dist'
});
// Deploy the function
const deployment = await af.functions().deploy({
functionId: 'function-id',
cid: codeUpload.pin.cid,
sgx: true, // optional: enable SGX
blake3Hash: 'hash', // optional: for verification
assetsCid: 'assets-cid' // optional: static assets
});
console.log('Deployment ID:', deployment.id);Listing Functions
# List all functions
af functions list// List all functions
const functions = await af.functions().list();
functions.forEach(func => {
console.log(`${func.name} (${func.slug})`);
console.log(`Status: ${func.status}`);
console.log(`URL: https://${func.slug}.af-functions.app`);
});Viewing Function Deployments
# List deployments for a function
af functions list-deployments --function-id <function-id>// List function deployments
const deployments = await af.functions().listDeployments({
functionId: 'function-id'
});
deployments.forEach(dep => {
console.log(`Deployment ${dep.id}`);
console.log(`CID: ${dep.cid}`);
console.log(`Created: ${dep.createdAt}`);
});Updating a Function
# Update function configuration
af functions update \
--function-id <function-id> \
--name new-name \
--routes '/api/v2/*=handler.js'// Update function
await af.functions().update({
id: 'function-id',
name: 'New Name',
routes: {
'/api/v2/*': 'handler.js'
},
status: 'ACTIVE' // or 'INACTIVE'
});Deleting a Function
# Delete a function
af functions delete --function-id <function-id>// Delete a function
await af.functions().delete({ id: 'function-id' });Route Configuration
Functions support flexible route matching:
{
routes: {
'/api/*': 'api.js', // Wildcard matching
'/users/:id': 'users.js', // Path parameters
'/blog/**': 'blog.js', // Recursive wildcard
'/exact': 'exact.js' // Exact match
}
}Environment Variables
Set environment variables for your functions:
export default async function handler(request) {
const apiKey = process.env.API_KEY;
const dbUrl = process.env.DATABASE_URL;
// Use environment variables
const response = await fetch('https://api.example.com', {
headers: {
'Authorization': `Bearer ${apiKey}`
}
});
return new Response(await response.text());
}Set variables during deployment or in your build process.
SGX (Software Guard Extensions)
Enable SGX for enhanced security and privacy:
af functions deploy \
--function-id <function-id> \
--code ./dist \
--sgxSGX provides:
- Encrypted Execution - Code runs in encrypted memory
- Attestation - Verify code hasn't been tampered with
- Confidential Computing - Protect sensitive data during processing
Function URLs
Access your functions via:
- Default URL:
https://<function-slug>.af-functions.app - Custom Domain: Configure custom domains for your functions
- Site Integration: Mount functions on specific routes within your site
Use Cases
API Backend
// REST API endpoint
export default async function handler(request) {
const url = new URL(request.url);
const id = url.searchParams.get('id');
const data = await fetchFromDatabase(id);
return new Response(JSON.stringify(data), {
headers: { 'Content-Type': 'application/json' }
});
}Form Processing
// Contact form handler
export default async function handler(request) {
if (request.method !== 'POST') {
return new Response('Method Not Allowed', { status: 405 });
}
const formData = await request.formData();
const email = formData.get('email');
const message = formData.get('message');
// Send email or store in database
await sendEmail({ to: 'contact@example.com', email, message });
return new Response(JSON.stringify({ success: true }), {
headers: { 'Content-Type': 'application/json' }
});
}Image Processing
// Dynamic image resizing
export default async function handler(request) {
const url = new URL(request.url);
const imageUrl = url.searchParams.get('url');
const width = url.searchParams.get('width');
const response = await fetch(imageUrl);
const image = await response.arrayBuffer();
// Resize image
const resized = await resizeImage(image, { width });
return new Response(resized, {
headers: { 'Content-Type': 'image/jpeg' }
});
}Authentication Middleware
// JWT authentication
export default async function handler(request) {
const token = request.headers.get('Authorization')?.split(' ')[1];
if (!token) {
return new Response('Unauthorized', { status: 401 });
}
try {
const user = await verifyToken(token);
return new Response(JSON.stringify({ user }), {
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
return new Response('Invalid Token', { status: 401 });
}
}Best Practices
TIP
- Keep functions small - One function per route or feature
- Use caching - Cache responses when possible
- Handle errors - Always return appropriate status codes
- Set CORS headers - Allow cross-origin requests when needed
- Monitor performance - Track function execution time and errors
- Use environment variables - Never hardcode secrets
Performance Tips
- Minimize dependencies - Only import what you need
- Use streaming - Stream large responses
- Cache responses - Set appropriate Cache-Control headers
- Optimize cold starts - Keep initialization code minimal
- Use edge caching - Leverage CDN caching
Troubleshooting
Function Not Responding
Problem: Function returns 500 or times out
Solutions:
- Check function logs for errors
- Verify all dependencies are included
- Test function locally first
- Check external API availability
CORS Errors
Problem: Browser blocks requests to function with errors like:
- "Access to fetch has been blocked by CORS policy"
- "No 'Access-Control-Allow-Origin' header is present"
- "CORS preflight request failed"
What is CORS?
CORS (Cross-Origin Resource Sharing) is a security feature that browsers use to prevent websites from making requests to different domains without permission. When your web app (e.g., myapp.com) tries to call your function (e.g., function.af-functions.app), the browser blocks it unless your function explicitly allows it.
Solution 1: Basic CORS Headers
Add CORS headers to all responses:
export default async function handler(request) {
const data = { message: 'Hello' };
return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*', // Allow all origins
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
}
});
}Solution 2: Handle Preflight Requests
Browsers send an OPTIONS request before the actual request (called "preflight"). You must handle this:
export default async function handler(request) {
// Handle preflight requests
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: {
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
'Access-Control-Max-Age': '86400', // Cache preflight for 24 hours
}
});
}
// Handle actual request
const data = { message: 'Hello' };
return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization'
}
});
}Solution 3: Specific Origin (Recommended for Production)
Instead of allowing all origins (*), specify your domain:
const allowedOrigins = [
'https://myapp.com',
'https://staging.myapp.com',
'http://localhost:3000' // For development
];
export default async function handler(request) {
const origin = request.headers.get('Origin');
const corsHeaders = {
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
};
// Check if origin is allowed
if (origin && allowedOrigins.includes(origin)) {
corsHeaders['Access-Control-Allow-Origin'] = origin;
}
// Handle preflight
if (request.method === 'OPTIONS') {
return new Response(null, {
status: 204,
headers: corsHeaders
});
}
// Handle actual request
const data = { message: 'Hello' };
return new Response(JSON.stringify(data), {
headers: {
'Content-Type': 'application/json',
...corsHeaders
}
});
}Solution 4: Helper Function
Create a reusable helper:
function corsResponse(body, options = {}) {
return new Response(body, {
...options,
headers: {
'Content-Type': 'application/json',
'Access-Control-Allow-Origin': '*',
'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, OPTIONS',
'Access-Control-Allow-Headers': 'Content-Type, Authorization',
...options.headers
}
});
}
export default async function handler(request) {
if (request.method === 'OPTIONS') {
return corsResponse(null, { status: 204 });
}
const data = { message: 'Hello' };
return corsResponse(JSON.stringify(data));
}Common CORS Headers Explained:
Access-Control-Allow-Origin: Which domains can access your function*= all domains (use for public APIs)https://myapp.com= specific domain (use for private APIs)
Access-Control-Allow-Methods: Which HTTP methods are allowed (GET, POST, etc.)Access-Control-Allow-Headers: Which headers the client can sendAccess-Control-Max-Age: How long (in seconds) to cache the preflight response
Security Note
For production, always specify exact origins instead of using *. This prevents unauthorized websites from calling your functions.
Slow Performance
Problem: Function execution is slow
Solutions:
- Optimize database queries
- Add caching layer
- Reduce payload size
- Use connection pooling
Next Steps
- Sites - Deploy static sites
- Storage - Store function data
- Custom Domains - Add custom domains
- Best Practices - Optimization tips