Edge Computing Starter
Production-ready template for building serverless applications with Cloudflare Workers and Hono
π Overview
Edge Computing Starter is a comprehensive, production-ready template for building serverless applications at the edge. It combines Cloudflare Workers for global deployment with Hono for elegant TypeScript routing, providing everything you need to build fast, scalable applications.
β¨ Features
- Cloudflare Workers: Deploy to 300+ locations globally
- Hono Framework: Ultra-fast web framework with TypeScript support
- TypeScript: Full type safety and excellent DX
- Zero Config: Works out of the box with sensible defaults
- Best Practices: Production-ready with error handling, logging, testing
- CI/CD Ready: GitHub Actions workflows included
- Database Support: D1, R2, and KV integration examples
- Authentication: Built-in auth patterns (JWT, OAuth, API keys)
- OpenAPI: Auto-generated API documentation
- Testing: Vitest for unit and integration tests
ποΈ Quick Start
Using CLI (Recommended)
npm create edge-app@latest my-app
cd my-app
npm run dev
Manual Clone
git clone https://github.com/groovy-web/edge-computing-starter.git my-app
cd my-app
npm install
npm run dev
Your app will be available at http://localhost:8787
π Project Structure
edge-computing-starter/
βββ src/
β βββ index.ts # Entry point
β βββ routes/ # API routes
β β βββ index.ts # Route aggregation
β β βββ auth.ts # Authentication routes
β β βββ users.ts # Example user routes
β β βββ health.ts # Health check
β βββ middleware/ # Custom middleware
β β βββ auth.ts # Auth middleware
β β βββ error.ts # Error handling
β β βββ logger.ts # Request logging
β βββ services/ # Business logic
β β βββ database.ts # DB client
β β βββ cache.ts # KV cache
β βββ lib/ # Utilities
β β βββ env.ts # Environment variables
β β βββ validator.ts # Input validation
β βββ types/ # TypeScript types
βββ tests/ # Test files
βββ wrangler.toml # Cloudflare config
βββ package.json
βββ tsconfig.json
π― Usage Examples
Basic Routing
import { Hono } from 'hono';
const app = new Hono();
app.get('/', (c) => {
return c.json({ message: 'Hello from the edge!' });
});
app.get('/api/hello/:name', (c) => {
const name = c.req.param('name');
return c.json({ greeting: `Hello, ${name}!` });
});
export default app;
Middleware
import { cors } from 'hono/cors';
import { logger } from 'hono/logger';
app.use('*', cors());
app.use('*', logger());
// Custom auth middleware
app.use('/api/protected/*', async (c, next) => {
const token = c.req.header('Authorization');
if (!token) {
return c.json({ error: 'Unauthorized' }, 401);
}
await next();
});
Database (D1)
import { getDB } from './services/database';
app.get('/api/users/:id', async (c) => {
const db = getDB(c.env.DB);
const user = await db
.prepare('SELECT * FROM users WHERE id = ?')
.bind(c.req.param('id'))
.first();
if (!user) {
return c.json({ error: 'User not found' }, 404);
}
return c.json(user);
});
KV Cache
app.get('/api/data', async (c) => {
const cache = c.env.CACHE;
// Try to get from cache
const cached = await cache.get('data:key', 'json');
if (cached) {
return c.json({ data: cached, cached: true });
}
// Generate fresh data
const data = await fetchData();
// Cache for 60 seconds
await cache.put('data:key', JSON.stringify(data), {
expirationTtl: 60,
});
return c.json({ data, cached: false });
});
R2 Storage
app.put('/api/upload', async (c) => {
const formData = await c.req.formData();
const file = formData.get('file') as File;
const key = `uploads/${Date.now()}-${file.name}`;
await c.env.R2.put(key, file.stream());
return c.json({
success: true,
url: `${c.env.R2_PUBLIC_URL}/${key}`,
});
});
Validation with Zod
import { z } from 'zod';
import { zValidator } from '@hono/zod-validator';
const userSchema = z.object({
name: z.string().min(2),
email: z.string().email(),
age: z.number().min(18),
});
app.post('/api/users', zValidator('json', userSchema), async (c) => {
const data = c.req.valid('json');
// Save to database
return c.json({ success: true, user: data }, 201);
});
π§ Configuration
Environment Variables
# Copy example env file
cp wrangler.example.toml wrangler.toml
Edit wrangler.toml:
name = "my-edge-app"
main = "src/index.ts"
compatibility_date = "2024-01-01"
[env.production]
vars = { ENVIRONMENT = "production" }
[[env.production.d1_databases]]
binding = "DB"
database_name = "my-db"
database_id = "your-database-id"
[[env.production.r2_buckets]]
binding = "R2"
bucket_name = "my-bucket"
Local Development
# Start dev server
npm run dev
# Run with local D1 database
npm run dev:local
# Run tests
npm test
# Type checking
npm run type-check
# Linting
npm run lint
# Deploy to Cloudflare
npm run deploy
π Available Scripts
| Script | Description |
|---|---|
dev |
Start development server |
dev:local |
Start with local D1 database |
deploy |
Deploy to Cloudflare Workers |
deploy:production |
Deploy to production |
test |
Run tests |
test:watch |
Watch mode for tests |
test:coverage |
Generate coverage report |
type-check |
TypeScript type checking |
lint |
Run ESLint |
lint:fix |
Fix linting issues |
format |
Format code with Prettier |
wrangler |
Run Wrangler CLI commands |
π§ͺ Testing
import { describe, it, expect } from 'vitest';
import { app } from '../src/index';
describe('API Routes', () => {
it('should return hello message', async () => {
const res = await app.request('/');
expect(res.status).toBe(200);
const json = await res.json();
expect(json.message).toBe('Hello from the edge!');
});
it('should handle protected routes', async () => {
const res = await app.request('/api/protected/data');
expect(res.status).toBe(401);
});
});
π Deployment
Automatic Deployment
Push to main branch triggers automatic deployment via GitHub Actions.
Manual Deployment
# Deploy to production
npm run deploy
# Deploy specific environment
npx wrangler deploy --env production
π Advanced Examples
WebSockets
app.get('/ws', upgradeWebSocket(() => ({
onMessage(evt, ws) {
ws.send(JSON.stringify({ echo: evt.data }));
},
onClose() {
console.log('Connection closed');
},
})));
Scheduled Tasks (Cron Triggers)
export default {
fetch: app.fetch,
scheduled: async (event, env, ctx) => {
// Run daily cleanup
await cleanupOldData(env.DB);
},
};
Email Sending (with MailChannels)
app.post('/api/send-email', async (c) => {
const message = {
from: '[email protected]',
to: '[email protected]',
subject: 'Hello',
content: 'Hello from Cloudflare Workers!',
};
await fetch('https://api.mailchannels.net/tx/v1/send', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(message),
});
return c.json({ success: true });
});
π Authentication Patterns
API Key
const apiKeyMiddleware = async (c, next) => {
const apiKey = c.req.header('X-API-Key');
const valid = await c.env.DB
.prepare('SELECT * FROM api_keys WHERE key = ?')
.bind(apiKey)
.first();
if (!valid) {
return c.json({ error: 'Invalid API key' }, 401);
}
await next();
};
JWT
import { verify } from 'jsonwebtoken';
const jwtMiddleware = async (c, next) => {
const token = c.req.header('Authorization')?.replace('Bearer ', '');
try {
const decoded = verify(token, c.env.JWT_SECRET);
c.set('user', decoded);
await next();
} catch (err) {
return c.json({ error: 'Invalid token' }, 401);
}
};
π Monitoring & Logging
// Custom logger middleware
import {θ―ε’ } from 'hono';
app.use('*', async (c, next) => {
const start = Date.now();
await next();
const duration = Date.now() - start;
console.log(JSON.stringify({
method: c.req.method,
path: c.req.path,
status: c.res.status,
duration,
timestamp: new Date().toISOString(),
}));
});
π€ Contributing
We welcome contributions! See CONTRIBUTING.md
π License
MIT License - see LICENSE
π Resources
β Support
If you find this helpful, please give us a star!
Made with β€οΈ by Groovy Web
Related Repositories
Explore more open-source tools from Groovy Web:
- langchain-multi-agent-example -- Multi-agent systems tutorial with LangChain
- rag-system-pgvector -- Production RAG with PostgreSQL + pgvector
- rag-systems-production -- Enterprise-grade RAG systems
- ai-testing-mcp -- AI testing via Model Context Protocol
- edge-computing-starter -- Cloudflare Workers + Hono template
- claude-code-workflows -- Workflows for Claude Code
- groovy-web-ai-agents -- Production AI agent configs
- groovy-web-examples -- Groovy/Grails examples