| name | cloudflare |
| description | [Applies to: **/*] Definitive guidelines for building secure, performant, and maintainable applications on Cloudflare's developer platform, emphasizing tiny bundles, edge-first design, and robust security. |
| source | cursor_mdc |
cloudflare Best Practices
Cloudflare's developer platform thrives on speed, security, and global distribution. Our focus is on building tiny, observable, and secure bundles that leverage the edge. This guide outlines the definitive best practices for our team.
1. Code Organization and Structure
Prioritize Workers & Pages for all new applications. Use the official wrangler CLI for project scaffolding and deployment.
- Project Initialization:
- Monorepos: For projects with multiple Workers or Pages apps, use
pnpm or yarn workspaces.
- Environment Variables: Manage secrets securely via
wrangler.toml or the Cloudflare dashboard. Use .dev.vars for local development.
- ❌ BAD: Storing sensitive data directly in code or
.env files that aren't .dev.vars.
- ✅ GOOD: Define variables in
wrangler.toml and reference them in your Worker.
name = "my-worker"
main = "src/index.ts"
compatibility_date = "2025-12-01"
[vars]
MY_API_KEY = "your-api-key-value"
[secrets]
- ✅ GOOD: Use
.dev.vars for local secrets, and integrate with CLI tools.
DATABASE_URL="postgresql://user:pass@localhost:5432/mydb"
dotenv -e .dev.vars -- npx prisma migrate dev
2. Common Patterns and Anti-patterns
Design for the edge: stateless Workers, stateful Durable Objects.
- Edge-first Design: Workers are stateless and globally distributed. Avoid in-memory state that needs to persist across requests or instances.
- ❌ BAD: Relying on global variables for user sessions or shared data.
let requestCount = 0;
export default {
async fetch(request, env, ctx) {
requestCount++;
return new Response(`Requests: ${requestCount}`);
},
};
- ✅ GOOD: Use KV for simple key-value storage, D1 for relational data, or Durable Objects for strong consistency and real-time state.
export default {
async fetch(request, env, ctx) {
let count = parseInt(await env.REQUEST_COUNTER.get("total_requests") || "0");
await env.REQUEST_COUNTER.put("total_requests", String(count + 1));
return new Response(`Requests: ${count + 1}`);
},
};
- Serverless SQL (D1, Hyperdrive): Use D1 for new, natively serverless relational databases. For existing regional databases, use Hyperdrive for edge acceleration.
- ✅ GOOD: D1 for new projects.
3. Performance Considerations
Bundle size and cold start latency are paramount.
- Bundle Size Optimization: Keep Worker bundles as small as possible. Tree-shake dependencies aggressively.
- Prisma Optimization: For ORMs, use Prisma with
engineType: "client" and an edge-compatible driver adapter. This avoids large Rust binaries.
- ❌ BAD: Default Prisma setup with Rust query engines in Workers.
- ✅ GOOD: Configure
schema.prisma and use an adapter.
// schema.prisma
generator client {
provider = "prisma-client-js"
engineType = "client" # Crucial for Workers
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
import { PrismaClient } from '@prisma/client/edge';
import { withAccelerate } from '@prisma/extension-accelerate';
const prisma = new PrismaClient({
datasourceUrl: env.DATABASE_URL,
}).$extends(withAccelerate());
- Caching: Leverage Cloudflare's CDN for static assets and API responses.
- ✅ GOOD: Set appropriate
Cache-Control headers.
return new Response(JSON.stringify(data), {
: {
: ,
: ,
},
});
4. Common Pitfalls and Gotchas
Understand the Workers runtime: single-threaded, event-driven, no Node.js APIs.
- Blocking I/O: Workers are single-threaded and event-driven. All I/O operations must be
awaited.
- Missing Security Policies: Always configure WAF, Bot Fight Mode, and Rate Limiting.
- ❌ BAD: Deploying public APIs without edge security.
- ✅ GOOD: Implement granular rate limiting for login endpoints, API abuse, and
cf_clearance cookie reuse.
5. Testing Approaches
Test early, test often, and test at the edge.
- Unit Testing: Use standard JavaScript/TypeScript testing frameworks (
Vitest, Jest) for pure functions and business logic.
- ✅ GOOD: Isolate and test small components.
import { expect, test } from 'vitest';
import { calculateDiscount } from './my-logic';
test('calculates discount correctly', () => {
expect(calculateDiscount(100, 0.1)).toBe(90);
});
- Integration Testing (Miniflare): Use
Miniflare for local emulation of the Workers runtime, including bindings (KV, D1, R2). This is critical for testing Worker logic with dependencies.
- ✅ GOOD: Simulate the Cloudflare environment locally.
import { Miniflare } from 'miniflare';
import { test, expect, beforeAll, afterAll } from 'vitest';
let mf: Miniflare;
beforeAll(async () => {
mf = new Miniflare({
modules: true,
scriptPath: 'dist/index.mjs',
bindings: { MY_VAR: 'test-value' },
: [],
: [],
: [],
});
});
( () => mf.());
(, () => {
res = mf.();
( res.()).();
});