Code and Structure Standards
Coding standards and repository structure for Cloudflare Experiments
Contributions should follow the project's coding and structure rules to keep the repository consistent and maintainable.
Experiment Structure
Every experiment lives under apps/experiments/<name>/ with this standardized layout:
Directory purposes:
Directory Breakdown
Core Principles
No shared code between experiments: Each experiment is standalone with its own package.json and dependencies. Do not import from other experiments or the repo root.
Single responsibility: One experiment = one Cloudflare capability (e.g. Workers AI, Browser Rendering, D1).
Edge-first, under ~60 seconds: Prefer stateless, fast request paths that complete quickly.
TypeScript and API Style
Strict TypeScript
- Use strict TypeScript; avoid
any - Type Worker env in
src/types/env.d.tsand useHono<{ Bindings: Env }> - All types should be explicitly defined
Error Handling
Use a shared helper for client errors:
jsonError(c, message, code, status);Status code guidelines:
400for bad request (invalid input)404for not found502for server/upstream errors (don't expose internals)
Example:
if (!url) {
return jsonError(c, "Missing or invalid query parameter: url", "INVALID_URL");
}Success Responses
Use jsonSuccess(c, data) for JSON success responses:
return jsonSuccess(c, { status: "reachable", responseTime: 87 });Global Error Handler
In src/index.ts, register app.onError(...) so uncaught errors return consistent JSON:
app.onError((err, c) => {
return c.json({ error: err.message, code: "INTERNAL_ERROR" }, 500);
});URL Parameter Validation
For any endpoint that takes a url query param:
- Validate with a shared
validateUrl(input)insrc/lib/url.ts - Allow only
http://andhttps:// - Return
jsonErrorwith a clear message and code (e.g.INVALID_URL) when invalid
Example:
const url = validateUrl(urlParam);
if (!url) {
return jsonError(c, "Missing or invalid query parameter: url", "INVALID_URL");
}Naming Conventions
- Error codes: Descriptive uppercase with underscores (e.g.
INVALID_URL,FETCH_ERROR,INTERNAL_ERROR) - Routes: Files in
src/routes/with kebab-case filenames matching the path (e.g.check.tsfor/check) - Functions: camelCase for functions and variables
- Types: PascalCase for interfaces and types
Real-World Examples
Error Handling Pattern
From src/lib/fetch.ts in the is-it-down experiment:
export async function fetchWithTiming(url: string): Promise<FetchResult> {
const start = Date.now();
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
try {
const res = await fetch(url, {
method: "GET",
signal: controller.signal,
headers: { "User-Agent": "Cloudflare-Experiments-IsItDown/1.0" },
});
const responseTimeMs = Date.now() - start;
clearTimeout(timeoutId);
return {
ok: res.ok,
statusCode: res.status,
responseTimeMs,
};
} catch (e) {
clearTimeout(timeoutId);
const responseTimeMs = Date.now() - start;
const message = e instanceof Error ? e.message : "Unknown error";
return {
ok: false,
statusCode: 0,
responseTimeMs,
error: message,
};
}
}Database Operations with Error Handling
From src/routes/shorten.ts in the link-shortener experiment:
let code = generateCode();
const maxAttempts = 5;
for (let attempt = 0; attempt < maxAttempts; attempt++) {
try {
await db.prepare("INSERT INTO links (code, url) VALUES (?, ?)").bind(code, url).run();
await setCachedLink(c.env, code, url);
return c.json({ code, url } satisfies ShortenResponse, 201);
} catch (e) {
const err = e as { message?: string };
if (err.message?.includes("UNIQUE constraint")) {
code = generateCode();
continue;
}
throw e;
}
}
return jsonError(c, "Could not generate unique code", "INTERNAL_ERROR", 502);Configuration Files
wrangler.toml/wrangler.json
Declare all Cloudflare bindings (D1, KV, R2, AI, etc.) in this file. These bindings must also be typed in src/types/env.d.ts.
package.json
Each experiment has its own dependencies. Common dependencies:
hono- Web framework@cloudflare/workers-types- TypeScript types for Workers
tsconfig.json
Enable strict mode and configure paths appropriately for the experiment.
Quality Checklist
Before submitting a PR, ensure:
- TypeScript strict mode is enabled with no
anytypes - All errors use
jsonErrorwith descriptive codes - Global error handler is registered in
index.ts - URL parameters are validated with
validateUrl - Environment bindings are declared in both
wrangler.tomlandsrc/types/env.d.ts - Routes are in
src/routes/, logic insrc/lib/, helpers insrc/utils/ - The experiment can be deployed with
npx wrangler deploy - README.md documents the API and usage
Next Steps
- Learn how to add new experiments
- Review the Contributing Guide
- Explore existing experiments in the repository