Quick Start
Get your first serverless function deployed to the edge in under 5 minutes using the EdgeAPI CLI.
Install the CLI
Install the EdgeAPI command-line tool globally via npm:
npm install -g @edgeapi/cli
Initialize a Project
Create a new EdgeAPI project with the interactive setup wizard:
edgeapi init my-first-function
cd my-first-function
This creates a project directory with:
edgeapi.toml — Project configuration (name, regions, environment variables)
src/index.ts — Your handler function (TypeScript by default)
.gitignore — Git configuration
package.json — Dependencies (if JavaScript/TypeScript)
Write a Handler
The handler is a simple async function that receives a request and returns a response:
// src/index.ts
export default {
async fetch(request: Request) {
const url = new URL(request.url);
if (url.pathname === '/api/hello') {
return new Response(
JSON.stringify({
message: 'Hello from the edge',
timestamp: new Date().toISOString(),
region: request.headers.get('cf-ray') || 'unknown'
}),
{ headers: { 'content-type': 'application/json' } }
);
}
return new Response('Not found', { status: 404 });
}
};
Deploy
Deploy to all 320 regions with a single command:
edgeapi deploy
You should see output like:
$ edgeapi deploy
→ Building project: my-first-function
✓ Build successful (156 KB)
→ Deploying to 320 regions...
✓ Deployed to us-west (2.3s)
✓ Deployed to eu-central (2.1s)
✓ Deployed to ap-southeast (2.4s)
... (317 more regions)
✓ All regions ready (18s total)
URL: https://my-first-function.edgeapi.date
Test Your Function
Once deployed, test your function:
curl https://my-first-function.edgeapi.date/api/hello
Response:
{
"message": "Hello from the edge",
"timestamp": "2026-05-05T14:32:18.902Z",
"region": "sfo-west"
}
Local Development
Use the edgeapi dev command to run your function locally with hot-reload:
edgeapi dev
Your function runs at http://localhost:8787 with automatic reload on file changes.
Runtimes
EdgeAPI supports multiple runtimes, each optimized for different use cases. All runtimes share the same Web Standard APIs for maximum portability.
TypeScript / JavaScript
The primary runtime for EdgeAPI. Full support for async/await, ES2022 syntax, and Web APIs.
Memory limits: 128 MB to 3 GB | CPU: Shared (up to 4 cores on 3GB tier) | Cold start: ~5ms
export default {
async fetch(request: Request, env: any, ctx: ExecutionContext) {
const data = await request.json();
const response = await fetch('https://api.example.com/process', {
method: 'POST',
body: JSON.stringify(data),
headers: { 'content-type': 'application/json' }
});
return response;
}
};
Python 3.12
Full CPython runtime with 2,500+ pre-installed packages (NumPy, Pandas, requests, etc.). Ideal for data processing and ML inference.
Memory limits: 256 MB to 3 GB | CPU: Shared | Cold start: ~80ms
import json
from datetime import datetime
async def on_fetch(request):
data = await request.json()
result = {
'processed': True,
'timestamp': datetime.now().isoformat(),
'input_size': len(data)
}
return Response(json.dumps(result), headers={'content-type': 'application/json'})
Rust
Compile Rust to WebAssembly for maximum performance and security. Rust code runs in a sandboxed WASM environment.
Memory limits: 64 MB to 512 MB | CPU: Dedicated core | Cold start: ~2ms
use wasm_bindgen::prelude::*;
use web_sys::Request;
#[wasm_bindgen]
pub async fn fetch(req: Request) -> String {
format!("Hello from Rust at {}", now())
}
Go (TinyGo to WASM)
Use Go with TinyGo for WASM compilation. Ideal for concurrent workloads compiled to a small binary.
Memory limits: 128 MB to 256 MB | CPU: Dedicated core | Cold start: ~4ms
package main
import (
"net/http"
"time"
)
func fetch(w http.ResponseWriter, r *http.Request) {
w.Header().Set("content-type", "application/json")
w.Write([]byte("{\"timestamp\":\"" + time.Now().String() + "\"}"))
}
Deno
Modern JavaScript/TypeScript runtime with native TypeScript support, no build step required.
Memory limits: 128 MB to 2 GB | CPU: Shared | Cold start: ~20ms
export default {
async fetch(req: Request) {
const { pathname } = new URL(req.url);
if (pathname === '/health') {
return new Response('OK');
}
return new Response('Not found', { status: 404 });
}
};
Bun
Fast JavaScript runtime with native TypeScript, JSX, and SQLite support. Zero-dependency bundler.
Memory limits: 128 MB to 2 GB | CPU: Shared | Cold start: ~15ms
export default {
async fetch(req: Request) {
if (req.method === 'POST') {
const body = await req.json();
return Response.json({ received: body });
}
return new Response('Method not allowed', { status: 405 });
}
};
Package Management
JavaScript/TypeScript: Use npm/yarn. Packages are bundled automatically during deployment.
Python: Use requirements.txt. Specify packages in your project root, they are installed during build.
Rust: Use Cargo.toml. Dependencies compiled with your code.
Go: Use go.mod. All deps compiled into the final WASM binary.
CLI Reference
The edgeapi command-line tool provides full project management, deployment, and debugging capabilities.
edgeapi init
Initialize a new project with guided setup:
edgeapi init [project-name] [--runtime ts|py|rust|go|deno|bun]
Creates a new project directory with handler, config, and dependencies. Defaults to TypeScript.
$ edgeapi init my-api
? Project name: my-api
? Runtime: TypeScript
? Regions to deploy: all
✓ Project created at ./my-api
edgeapi deploy
Deploy the current project to production:
edgeapi deploy [--regions us-west,eu-central] [--env production]
Builds, bundles, and deploys your function to specified regions (defaults to all 320).
$ edgeapi deploy
→ Validating project...
→ Building (TypeScript)...
✓ Build successful (142 KB)
→ Compressing...
✓ Compressed to 34 KB
→ Pushing to 320 regions...
✓ All regions ready (22s)
edgeapi logs
Stream logs from your deployed function in real-time:
edgeapi logs [--tail] [--level error|warn|info|debug] [--region us-west]
Use --tail to stream live logs. Filter by level or region.
$ edgeapi logs --tail
[2026-05-05 14:35:22] INFO GET /api/users — 145ms (us-west)
[2026-05-05 14:35:24] INFO POST /api/users — 324ms (eu-central)
[2026-05-05 14:35:26] ERROR Database timeout — (ap-southeast)
edgeapi dev
Run your function locally with hot-reload and debugging:
edgeapi dev [--port 8787] [--inspect]
Launches a local development server matching production behavior.
$ edgeapi dev
→ Starting development server...
✓ Running at http://localhost:8787
✓ Watching for changes...
edgeapi kv
Manage Edge KV namespaces and keys:
edgeapi kv put <namespace> <key> <value> [--ttl 3600]
edgeapi kv get <namespace> <key>
edgeapi kv delete <namespace> <key>
edgeapi kv list <namespace> [--prefix api:]
Examples:
$ edgeapi kv put cache user:123 '{"name":"Alice"}'
$ edgeapi kv get cache user:123
{"name":"Alice"}
$ edgeapi kv list cache --prefix user:
user:123
user:456
user:789
edgeapi secrets
Manage encrypted environment secrets:
edgeapi secrets set API_KEY "sk-..."
edgeapi secrets list
edgeapi secrets delete API_KEY
Secrets are encrypted and injected at runtime. They are never logged or exposed in deployments.
edgeapi domains
Manage custom domains and SSL certificates:
edgeapi domains add my-app.com
edgeapi domains list
edgeapi domains remove my-app.com
Management API
The EdgeAPI Management API provides programmatic access to deployments, projects, logs, and regions. Base URL: https://edgeapi.date/api/v1
Authentication
All API requests require bearer token authentication in the Authorization header:
Authorization: Bearer sk_prod_abc123...
Generate API keys in the dashboard at /dashboard/settings/api-keys
List Regions
GET
/api/v1/regions
Returns all available deployment regions.
Response
{
"regions": [
{
"id": "us-west",
"name": "US West (California)",
"datacenter": "LAX1",
"latency_ms": 12,
"status": "operational"
},
{
"id": "eu-central",
"name": "EU Central (Frankfurt)",
"datacenter": "FRA1",
"latency_ms": 145,
"status": "operational"
},
{
"id": "ap-southeast",
"name": "Asia Pacific (Singapore)",
"datacenter": "SIN1",
"latency_ms": 78,
"status": "operational"
}
],
"total": 320
}
Create Project
POST
/api/v1/projects
Create a new project. Requires name and runtime.
Request Body
{
"name": "my-api",
"runtime": "typescript",
"regions": ["us-west", "eu-central", "ap-southeast"],
"description": "User authentication API"
}
Response (201 Created)
{
"id": "proj_abc123",
"name": "my-api",
"runtime": "typescript",
"created_at": "2026-05-05T14:30:00Z",
"url": "https://my-api.edgeapi.date",
"regions": ["us-west", "eu-central", "ap-southeast"]
}
Create Deployment
POST
/api/v1/deployments
Deploy a function bundle. Accepts base64-encoded handler code.
Request Body
{
"project_id": "proj_abc123",
"handler": "base64_encoded_bundle_here",
"metadata": {
"version": "1.0.0",
"environment": "production"
}
}
Response (202 Accepted)
{
"deployment_id": "dep_xyz789",
"project_id": "proj_abc123",
"status": "pending",
"created_at": "2026-05-05T14:32:00Z",
"regions_target": 320,
"regions_ready": 0
}
Get Logs
GET
/api/v1/logs/:project_id
Retrieve logs for a project with optional filtering.
Query Parameters
limit=100 # Max results (default 50, max 1000)
offset=0 # Pagination offset
level=error|warn|info|debug
region=us-west # Filter by region
since=2026-05-05T14:00:00Z # ISO 8601 timestamp
tail=true # Stream logs (WebSocket upgrade)
Response
{
"logs": [
{
"timestamp": "2026-05-05T14:35:22.123Z",
"level": "info",
"region": "us-west",
"request_id": "req_abc123",
"message": "GET /api/users",
"duration_ms": 145
},
{
"timestamp": "2026-05-05T14:35:24.456Z",
"level": "error",
"region": "eu-central",
"request_id": "req_def456",
"message": "Database connection timeout",
"error_code": "ECONNREFUSED"
}
],
"total": 2,
"next_offset": 2
}
Example: cURL
Deploy using the API with cURL:
curl -X POST https://edgeapi.date/api/v1/deployments \
-H "Authorization: Bearer sk_prod_abc123" \
-H "Content-Type: application/json" \
-d '{
"project_id": "proj_abc123",
"handler": "ZXhwb3J0IGRlZmF1bHQgeyBhc3luYyBmZXRjaChyZXEpIHsgcmV0dXJuIG5ldyBSZXNwb25zZSgnSGknKTsgfSB9",
"metadata": { "version": "1.0.0" }
}'
Webhook Events
Receive real-time notifications about deployments and errors:
deployment.queued — Deployment received and queued
deployment.building — Build started
deployment.built — Build completed
deployment.ready — All regions ready
function.error — Function raised unhandled exception
region.offline — Region went offline
Edge KV
Edge KV is a globally distributed key-value store replicated across all 320 regions. It provides low-latency, eventually-consistent storage for application state, caches, and sessions.
Overview
Edge KV operates as follows:
- Eventual Consistency: Writes to one region propagate to all others within 2-5 seconds
- 320 Regions: Read from any region with ~<5ms latency
- TTL Support: Keys automatically expire after configured TTL
- Atomic Operations: Compare-and-swap (CAS) for distributed consensus
- Namespaces: Organize data with separate namespaces per project
Basic Operations
Access Edge KV via the env.EDGE_KV binding in your handler:
export default {
async fetch(request: Request, env: Env) {
const kv = env.EDGE_KV;
// Put a key
await kv.put('user:123:profile',
JSON.stringify({ name: 'Alice', email: 'alice@example.com' }),
{ expirationTtl: 3600 }
);
// Get a key
const profile = await kv.get('user:123:profile', 'json');
// Delete a key
await kv.delete('user:123:profile');
// List keys with prefix
const list = await kv.list({ prefix: 'user:', limit: 100 });
return new Response(JSON.stringify({ list }));
}
};
Advanced Patterns
Atomic Compare-and-Swap:
// Increment counter atomically
const counter = await kv.get('stats:views');
const newValue = ((counter ? parseInt(counter) : 0) + 1).toString();
await kv.put('stats:views', newValue, {
version: counter.version
});
Session Storage with TTL:
// Store session, auto-expire after 24 hours
await kv.put(
`session:${sessionId}`,
JSON.stringify({ userId: user.id, token: jwt }),
{ expirationTtl: 86400 }
);
Consistency Model
Edge KV provides eventual consistency with these guarantees:
- Writes are immediately visible in the same region
- Reads in other regions see the latest value within 2-5 seconds
- Deleted keys are purged from all regions within 5 seconds
- For strong consistency, use Edge D1 instead
CLI Commands
# Put a value
edgeapi kv put cache user:123 '{"name":"Alice"}' --ttl 3600
# Get a value
edgeapi kv get cache user:123
# Delete a key
edgeapi kv delete cache user:123
# List keys with prefix
edgeapi kv list cache --prefix api:
Edge D1
Edge D1 is a globally distributed SQLite database. Each region maintains a replica of your database, providing low-latency reads and strong consistency within a region.
Architecture
- SQLite: Full SQL support with familiar syntax
- Primary + Replicas: One primary region, replicas in all others
- Writes: Must route to primary (automatic failover to regional replica on primary failure)
- Reads: Always local in any region (~<1ms)
- Replication: ~100-500ms lag, tuned for consistency
Schema Migrations
Manage schema with migration files in migrations/:
-- migrations/001_initial.sql
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT UNIQUE NOT NULL,
name TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE posts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
title TEXT NOT NULL,
content TEXT,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id)
);
CREATE INDEX idx_posts_user_id ON posts(user_id);
Querying from Handlers
export default {
async fetch(request: Request, env: Env) {
const db = env.EDGE_D1;
// Simple query
const users = await db.prepare(
'SELECT * FROM users WHERE created_at > ?'
).bind(new Date(Date.now() - 86400000)).all();
// Insert with parameters (prevents SQL injection)
const result = await db.prepare(
'INSERT INTO users (email, name) VALUES (?, ?)'
).bind(email, name).run();
// Transaction
const tx = db.prepare('BEGIN TRANSACTION').run();
await db.prepare('INSERT INTO posts (user_id, title) VALUES (?, ?)')
.bind(userId, title).run();
await db.prepare('UPDATE users SET post_count = post_count + 1 WHERE id = ?')
.bind(userId).run();
db.prepare('COMMIT').run();
return new Response(JSON.stringify(users));
}
};
Prepared Statements
Always use prepared statements with parameter binding to prevent SQL injection:
// SAFE: Uses parameter binding
await db.prepare('SELECT * FROM users WHERE id = ?').bind(id).first();
// UNSAFE: String interpolation
// await db.exec(`SELECT * FROM users WHERE id = ${id}`); // DON'T DO THIS
Streaming & WebSockets
EdgeAPI supports HTTP streaming, Server-Sent Events (SSE), and WebSocket connections for real-time communication.
HTTP Streaming
Stream large responses without buffering:
export default {
async fetch(request: Request) {
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
// Write data asynchronously
(async () => {
for (let i = 0; i < 1000000; i++) {
await writer.write(new TextEncoder().encode(`${i}\n`));
}
await writer.close();
})();
return new Response(readable, {
headers: { 'content-type': 'text/plain' }
});
}
};
Server-Sent Events
Push updates to clients in real-time:
export default {
async fetch(request: Request) {
if (request.url.endsWith('/events')) {
const { readable, writable } = new TransformStream();
const writer = writable.getWriter();
// Send SSE events
(() => {
writer.write(new TextEncoder().encode(
'data: {\"message\":\"connected\"}\n\n'
));
let counter = 0;
const interval = setInterval(async () => {
await writer.write(new TextEncoder().encode(
`data: {"counter":${counter++}}\n\n`
));
}, 1000);
})();
return new Response(readable, {
headers: {
'content-type': 'text/event-stream',
'cache-control': 'no-cache'
}
});
}
}
};
WebSockets
Full-duplex WebSocket connections for bidirectional communication:
export default {
async fetch(request: Request) {
if (request.headers.get('upgrade') === 'websocket') {
const { socket, response } = Deno.upgradeWebSocket(request);
socket.onopen = () => {
socket.send(JSON.stringify({ type: 'connected' }));
};
socket.onmessage = (event) => {
const msg = JSON.parse(event.data);
socket.send(JSON.stringify({
type: 'echo',
data: msg
}));
};
socket.onclose = () => console.log('Connection closed');
socket.onerror = (err) => console.error(err);
return response;
}
}
};
Secrets & Environment Variables
Safely manage API keys, database passwords, and other sensitive configuration.
Setting Secrets
Use the CLI to set encrypted secrets:
edgeapi secrets set DATABASE_URL "postgresql://user:pass@localhost/db"
edgeapi secrets set API_KEY "sk_prod_abc123..."
edgeapi secrets list
edgeapi secrets delete API_KEY
Accessing in Code
Access secrets via the env parameter:
export default {
async fetch(request: Request, env: Env) {
const dbUrl = env.DATABASE_URL;
const apiKey = env.API_KEY;
const response = await fetch('https://api.example.com/data', {
headers: { 'Authorization': `Bearer ${apiKey}` }
});
return response;
}
};
Environment-Specific Config
Use edgeapi.toml for environment-specific values:
[env.production]
DATABASE_URL = "postgresql://prod-user:...@prod.db"
LOG_LEVEL = "error"
[env.staging]
DATABASE_URL = "postgresql://stage-user:...@stage.db"
LOG_LEVEL = "info"
Deploy to specific environment:
edgeapi deploy --env production
Security Best Practices
- Never hardcode secrets in code or config files
- Rotate API keys regularly
- Use least-privilege API keys (read-only when possible)
- Audit secret usage via logs
- Enable secret rotation reminders in dashboard settings
Observability
Monitor, debug, and understand your functions with logs, metrics, and distributed tracing.
Logs
All console.log(), console.error(), and console.warn() calls are automatically captured:
export default {
async fetch(request: Request) {
console.info(`Received ${request.method} ${request.url}`);
try {
const result = await processRequest(request);
console.log('Processing complete', { duration: 145, status: 'ok' });
return new Response(result);
} catch (err) {
console.error('Processing failed', { error: err.message });
return new Response('Error', { status: 500 });
}
}
};
View logs in real-time:
edgeapi logs --tail --level debug
Structured Logging
Log structured data for better filtering and analysis:
console.info(JSON.stringify({
event: 'user_login',
user_id: user.id,
email: user.email,
ip: request.headers.get('cf-connecting-ip'),
user_agent: request.headers.get('user-agent'),
timestamp: new Date().toISOString()
}));
Metrics
EdgeAPI automatically captures these metrics:
- Request count — Total requests per region, status code
- Latency — P50, P95, P99 response times
- Error rate — 4xx and 5xx error percentages
- Cold starts — Count and duration
- Memory usage — Peak and average per invocation
- CPU time — Execution time per region
Access metrics via the dashboard or API:
curl -H "Authorization: Bearer sk_prod_..." \
https://edgeapi.date/api/v1/metrics?project_id=proj_abc123&since=2026-05-05T00:00:00Z
Distributed Tracing (OpenTelemetry)
EdgeAPI supports OpenTelemetry for distributed tracing:
import { trace } from '@opentelemetry/api';
const tracer = trace.getTracer('my-app');
export default {
async fetch(request: Request, env: Env) {
const span = tracer.startSpan('handle-request');
const dbSpan = tracer.startSpan('query-database', {
parent: span
});
const data = await env.EDGE_D1.prepare(
'SELECT * FROM users WHERE id = ?'
).bind(userId).first();
dbSpan.end();
span.end();
return new Response(JSON.stringify(data));
}
};
Monitoring Dashboard
Access real-time monitoring at /dashboard/projects/:id/monitor:
- Live request stream with latencies
- Error rate and error log explorer
- Latency percentiles (P50, P95, P99, P99.9)
- Regional heatmap showing latency and error rates
- Memory and CPU usage trends
- Cost breakdown by region
Troubleshooting
Common issues and solutions when building with EdgeAPI.
Issue 1: Build Failures
Error: Build failed: Module not found: '@mylib/api'
Solution:
- Ensure all dependencies are listed in
package.json
- Run
npm install locally to verify dependencies resolve
- Check for circular imports or missing exports
- Use
edgeapi dev to test build locally before deploying
Issue 2: Runtime Errors
Error: ReferenceError: fetch is not defined
Solution:
- Ensure you're using a supported Web API (fetch, URL, Request, Response)
- Edge runtimes don't have Node.js built-ins (fs, path, etc.) — use alternatives:
- For file operations, use Edge KV or Edge D1
- For HTTP, use the standard Fetch API
- Review logs with
edgeapi logs --tail to see full error traces
Issue 3: Edge KV Consistency
Problem: Value written in us-west doesn't appear immediately in eu-central
Solution:
- Edge KV provides eventual consistency (2-5 second replication lag)
- For immediate consistency, read from the region where you wrote
- For global immediate consistency, use Edge D1 instead
- Use
env.EDGE_KV.put(..., { metadata: { sync: true } }) for critical writes (slightly higher latency)
Issue 4: Deployment Timeout
Error: Deployment timeout after 60s
Solution:
- Handler functions have a 5-second execution timeout
- Long-running operations should defer to background workers
- For large bundle sizes (>100 MB), consider splitting into multiple functions
- Check deployment logs:
edgeapi logs --region deployment-worker
Issue 5: Cold Start Latency
Problem: Some requests take 500ms+ to complete
Solution:
- EdgeAPI supports warm pools for low cold-start rates (<5%)
- First invocation in a region is a cold start (~50-200ms depending on runtime)
- Use
edgeapi metrics to measure cold start frequency
- Consider pre-warming: schedule periodic requests in low-traffic hours
- Minimize bundle size to reduce cold start penalty
Issue 6: Region Unavailability
Error: us-east region offline
Solution:
- Check the status dashboard:
/status/
- Enable automatic failover in settings: requests route to nearest available region
- Exclude specific regions from deployment:
edgeapi deploy --regions us-west,eu-central
- Subscribe to incident notifications for urgent updates