Code Conventions
This page documents the coding standards and conventions used across the Aero2 codebase.
Language
TypeScript everywhere. Both backend (Cloudflare Worker) and frontend (React) code is written in TypeScript with strict type checking enabled.
Linting and Formatting
Aero2 uses Biome for both linting and formatting:
# Check for lint errors
npm run lint
# Auto-fix lint errors
npm run lint:fixBiome configuration is in biome.json at the project root. CI fails on lint errors.
Backend Conventions
Hono Framework
The backend uses Hono for HTTP routing. The app is typed with environment bindings:
import { Hono } from "hono";
const app = new Hono<{ Bindings: Env }>();The Env type is generated from wrangler.json bindings using npm run cf-typegen, which produces worker-configuration.d.ts.
Request Validation
All request bodies and query parameters are validated using Zod schemas:
import { z } from "zod";
const createClientSchema = z.object({
name: z.string().min(1).max(255),
redirect_uris: z.array(z.string().url()),
scopes: z.array(z.string()),
grant_types: z.array(z.enum(["authorization_code", "refresh_token"])),
});
app.post("/api/clients", async (c) => {
const body = createClientSchema.parse(await c.req.json());
// ...
});Database Queries
All D1 queries use parameterized statements. Never interpolate user input into SQL strings:
// Correct
const user = await env.DB.prepare(
"SELECT * FROM users WHERE email = ? AND app_id = ?"
).bind(email, appId).first();
// WRONG - SQL injection risk
const user = await env.DB.prepare(
`SELECT * FROM users WHERE email = '${email}'`
).first();Error Handling
Return appropriate HTTP status codes with JSON error bodies. Never expose internal error details to clients:
// Good: generic error message
return c.json({ error: "Invalid credentials" }, 401);
// Bad: leaks internal details
return c.json({ error: `Database error: ${err.message}` }, 500);Log detailed errors server-side for debugging:
console.error("Token verification failed:", err);
return c.json({ error: "Authentication failed" }, 401);Frontend Conventions
React Components
- Use functional components with hooks (no class components)
- Use CSS Modules for component styling (
.module.cssfiles) - Components live in
src/frontend/components/; pages insrc/frontend/pages/
State Management
- Use React Context for shared state (AuthContext)
- Use useState and useReducer for local component state
- No external state management library (Redux, Zustand, etc.)
Naming Conventions
| Category | Convention | Example |
|---|---|---|
| Variables and functions | camelCase | getUserById, isAuthenticated |
| React components | PascalCase | LoginPage, UserCard |
| TypeScript types and interfaces | PascalCase | UserProfile, AuthState |
| Files (general) | kebab-case | auth-context.ts, rate-limit.ts |
| Files (React components) | PascalCase | Login.tsx, AdminUsers.tsx |
| CSS modules | kebab-case | login-page.module.css |
| Database columns | snake_case | created_at, app_id |
| API routes | kebab-case | /api/audit-logs, /api/api-keys |
| Environment variables | UPPER_SNAKE_CASE | MASTER_KEY, PLATFORM_DOMAIN |
Exports
- No default exports except for the Worker entry point (
export default app) and React page components - Use named exports for utilities, types, middleware, and shared components
- This ensures consistent import naming and better IDE support
// Good: named export
export function hashPassword(password: string): Promise<string> { ... }
// Only for Worker entry point
export default app;Comments
- Add comments for complex business logic, security-critical code, and non-obvious decisions
- Do not add comments for self-explanatory code
- Use
// TODO:for known technical debt (include a brief description of what needs to change)
Security Practices in Code
- Always include
app_idin database queries for tenant-scoped tables - Always validate user input with Zod schemas before processing
- Never log sensitive data (passwords, tokens, secrets)
- Use parameterized queries for all database operations
- Use constant-time comparison for secret verification
- Set appropriate cookie attributes (HttpOnly, Secure, SameSite)