An Edge-First Static Gallery with Cloudflare & Angular
PhotoFlare is a high-performance image gallery that demonstrates the power of edge computing by serving a fully static yet dynamic photo experience using Cloudflare's ecosystem and Angular—no traditional backend required.
- 🔗 Live Gallery - View the production gallery
- 📖 Blog Post - Detailed technical breakdown
- 🚀 WebP Optimization: Automatically serves optimized WebP images based on device and browser support
- 📱 Responsive Design: Seamless experience across all device types
- ⚡ Edge-Native Performance: Sub-100ms metadata lookups via Cloudflare KV
- 📸 High-Quality Downloads: Users can download original, highest-quality versions
- 🔗 Instagram Integration: Direct links to corresponding Instagram posts
- 🛡️ CORS Compliant: Proper cross-origin handling for secure API access
- 📦 Serverless Architecture: Fully scalable with no server maintenance
┌─────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ Client App │───▶│ Cloudflare Pages │───▶│ Cloudflare CDN │
│ (Angular) │ │ Static Hosting │ │ Global Edge │
└─────────────────┘ └──────────────────┘ └─────────────────┘
│ │ │
│ ▼ │
│ ┌─────────────────┐ │
└─────────────▶│ Cloudflare │◀─────────────┘
│ Worker (API) │
└─────────────────┘
│
┌────────────┴────────────┐
▼ ▼
┌─────────────────┐ ┌─────────────────┐
│ Cloudflare KV │ │ Cloudflare R2 │
│ (Metadata) │ │ (Images) │
└─────────────────┘ └─────────────────┘
| Purpose | Technology |
|---|---|
| Frontend UI | Angular |
| Static Hosting | Cloudflare Pages |
| Image Metadata | Cloudflare KV Store |
| Image Storage | Cloudflare R2 |
| Edge Logic | Cloudflare Workers |
| CI/CD | GitHub Actions |
- Node.js 18+ and npm
- Wrangler CLI (
npm install -g wrangler) - Cloudflare account with Pages, Workers, KV, and R2 enabled
git clone https://github.com/Ankitd013/PhotoFlare.git
cd PhotoFlarecd angular-app
npm install
ng serve # Development server on http://localhost:4200cd ../worker
npm install
wrangler dev # Local worker developmentwrangler kv:namespace create "PHOTOFLARE"Create your gallery metadata in KV with the key photos:
wrangler kv:key put --binding=PHOTOFLARE photos '[
{
"description": "Your photo description",
"instagramPostUrl": "https://instagram.com/p/YOUR_POST/",
"date": "2024-01-01",
"images": [
{"img": "original.jpg", "webp": "optimized_webp_name"}
]
}
]'Set up your R2 bucket for image storage:
- Original images in
/org/folder - WebP optimized images in
/img/folder
Update wrangler.toml:
name = "photoflare-worker"
main = "src/index.js"
compatibility_date = "2024-01-01"
[[kv_namespaces]]
binding = "PHOTOFLARE"
id = "your_kv_namespace_id"
# Add route for your custom domain
route = "pixels.yourdomain.com/docs/*"Update src/environments/environment.prod.ts:
export const environment = {
production: true,
bucketURL: 'https://your-r2-bucket.your-domain.com',
rawImagePath: 'org', // Original images folder
webpImagePath: 'img', // WebP images folder
dataURL: '/docs/' // API route served by Worker
};The Worker handles CORS and API routing:
export default {
async fetch(request, env, ctx) {
try {
// Fetch metadata from KV
let value = await env.PHOTOFLARE.get("photos");
// Configure CORS
const allowedOrigins = [
"http://localhost:4200",
"https://pixels.yourdomain.com"
];
const origin = request.headers.get("Origin");
const headers = new Headers({
"Access-Control-Allow-Origin": origin,
"Access-Control-Allow-Methods": "GET, OPTIONS",
"Access-Control-Allow-Headers": "Origin, X-Requested-With, Content-Type, Accept"
});
return new Response(value, { headers });
} catch (error) {
console.error('Error fetching from KV:', error);
return new Response('Internal Server Error', { status: 500 });
}
}
};The project includes GitHub Actions workflows for:
- Angular app → Cloudflare Pages
- Worker → Cloudflare Workers
- Secrets management → GitHub repository settings
# Deploy Angular app
cd angular-app
npm run build
wrangler pages deploy dist/
# Deploy Worker
cd ../worker
wrangler deploy- ⚡ Sub-100ms KV metadata lookups
- 🖼️ Seamless R2 image loading with lazy loading
- 🚀 Fully serverless and globally distributed
- 📱 Mobile-optimized with WebP format support
- Edge-Native Architecture: No traditional backend servers required
- Optimized Image Delivery: WebP format with fallback to original formats
- Dynamic Static Site: Feels dynamic while being completely static
- Global Performance: Leverages Cloudflare's 300+ edge locations
- Cost-Effective: Runs within Cloudflare's generous free tiers
- CORS properly configured for cross-origin requests
- No exposed API keys in frontend code
- Secure image serving through R2 public bindings
- Worker-based API routing for enhanced security
Gallery metadata stored in Cloudflare KV:
[
{
"description": "Photo description text",
"instagramPostUrl": "https://instagram.com/p/POST_ID/",
"date": "2024-01-01",
"images": [
{
"img": "DSCN0001.jpg",
"webp": "DSCN0001"
}
]
}
]- Fork the repository
- Create your feature branch (
git checkout -b feature/AmazingFeature) - Commit your changes (
git commit -m 'Add some AmazingFeature') - Push to the branch (
git push origin feature/AmazingFeature) - Open a Pull Request
This project is licensed under the CC BY 4.0 License - see the LICENSE file for details.
- Cloudflare for providing an amazing edge computing platform
- Angular team for the robust frontend framework
- Instagram API for social media integration capabilities
Ankit Das - @ankitd013
Project Link: https://github.com/Ankitd013/PhotoFlare
⭐ Star this repo if you found it helpful!
Built with ❤️ using edge-first architecture and modern web technologies.