This site is not affiliated with or endorsed by Cloudflare, Inc. It simply showcases experiments built using Cloudflare services.
Cloudflare Experiments

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:

index.ts
package.json
wrangler.json
tsconfig.json
README.md

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.ts and use Hono<{ 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:

  • 400 for bad request (invalid input)
  • 404 for not found
  • 502 for 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) in src/lib/url.ts
  • Allow only http:// and https://
  • Return jsonError with 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.ts for /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 any types
  • All errors use jsonError with descriptive codes
  • Global error handler is registered in index.ts
  • URL parameters are validated with validateUrl
  • Environment bindings are declared in both wrangler.toml and src/types/env.d.ts
  • Routes are in src/routes/, logic in src/lib/, helpers in src/utils/
  • The experiment can be deployed with npx wrangler deploy
  • README.md documents the API and usage

Next Steps

On this page