Skip to content

Code Style Guide

Temp edited this page Nov 3, 2025 · 1 revision

Code Style Guide

Coding standards and conventions for D1 Database Manager.

Overview

Consistent code style makes the codebase easier to read, maintain, and contribute to.

General Principles

  1. Readability - Code is read more than written
  2. Consistency - Follow existing patterns
  3. Simplicity - Prefer simple over clever
  4. Type Safety - Use TypeScript strictly
  5. DRY - Don't Repeat Yourself

TypeScript Standards

Type Annotations

Always use explicit types:

Good:

interface Database {
  id: string;
  name: string;
  created_at: string;
}

function loadDatabase(id: string): Promise<Database> {
  // ...
}

Bad:

function loadDatabase(id) {  // Missing types
  // ...
}

const data: any = await fetch();  // Using any

Avoid any

Use unknown or specific types:

Good:

function processData(data: unknown): string {
  if (typeof data === 'string') {
    return data;
  }
  return JSON.stringify(data);
}

Bad:

function processData(data: any): any {
  return data;
}

Interfaces vs Types

Prefer interfaces for objects:

interface User {
  id: number;
  name: string;
  email: string;
}

Use types for unions/intersections:

type View = 
  | { type: 'list' }
  | { type: 'database'; id: string };

Enums

Use const enums when possible:

const enum Status {
  Active = 'active',
  Inactive = 'inactive'
}

Or string literal unions:

type Status = 'active' | 'inactive' | 'pending';

React Standards

Functional Components

Always use functional components:

Good:

interface Props {
  title: string;
  onClose: () => void;
}

function Dialog({ title, onClose }: Props) {
  return (
    <div>
      <h2>{title}</h2>
      <button onClick={onClose}>Close</button>
    </div>
  );
}

Bad:

class Dialog extends React.Component {
  render() {
    return <div>...</div>;
  }
}

Hooks

Use hooks for state and effects:

function MyComponent() {
  const [count, setCount] = useState(0);
  
  useEffect(() => {
    document.title = `Count: ${count}`;
  }, [count]);
  
  return <button onClick={() => setCount(count + 1)}>{count}</button>;
}

Custom hooks for reusable logic:

function useLocalStorage<T>(key: string, initialValue: T) {
  const [value, setValue] = useState<T>(() => {
    const stored = localStorage.getItem(key);
    return stored ? JSON.parse(stored) : initialValue;
  });
  
  useEffect(() => {
    localStorage.setItem(key, JSON.stringify(value));
  }, [key, value]);
  
  return [value, setValue] as const;
}

Props Destructuring

Destructure props in function signature:

Good:

function Button({ label, onClick, variant = 'default' }: Props) {
  return <button onClick={onClick}>{label}</button>;
}

Bad:

function Button(props: Props) {
  return <button onClick={props.onClick}>{props.label}</button>;
}

Event Handlers

Name with handle prefix:

function Form() {
  const handleSubmit = (e: FormEvent) => {
    e.preventDefault();
    // Handle submission
  };
  
  const handleChange = (e: ChangeEvent<HTMLInputElement>) => {
    setValue(e.target.value);
  };
  
  return <form onSubmit={handleSubmit}>...</form>;
}

Naming Conventions

Files

Components: PascalCase

DatabaseView.tsx
TableOperations.tsx
QueryConsole.tsx

Utilities: camelCase

api.ts
helpers.ts
auth.ts

Configuration: lowercase with dashes

vite.config.ts
tailwind.config.js
wrangler.toml

Variables

camelCase for variables:

const userName = 'Alice';
const isActive = true;
const totalCount = 100;

UPPER_SNAKE_CASE for constants:

const API_BASE_URL = 'https://api.example.com';
const MAX_RETRIES = 3;
const DEFAULT_PAGE_SIZE = 50;

Functions

camelCase with verb prefix:

function loadDatabases() { }
function createTable() { }
function handleClick() { }
function validateInput() { }

Components

PascalCase:

function DatabaseView() { }
function TableGrid() { }
function FilterBar() { }

Interfaces/Types

PascalCase:

interface Database { }
type QueryResult = { };
interface TableViewProps { }

Code Organization

File Structure

One component per file:

DatabaseView.tsx  # Only DatabaseView component
TableGrid.tsx     # Only TableGrid component

Utility functions grouped:

helpers.ts  # Related helper functions
filters.ts  # Filter-related utilities

Import Order

Group imports:

// 1. React/external libraries
import { useState, useEffect } from 'react';
import { Button } from '@/components/ui/button';

// 2. Internal modules
import { api } from '@/services/api';
import { formatDate } from '@/lib/utils';

// 3. Types
import type { Database, Table } from '@/types';

// 4. Styles (if any)
import './styles.css';

Component Structure

Consistent ordering:

function MyComponent({ prop1, prop2 }: Props) {
  // 1. Hooks
  const [state, setState] = useState();
  const customHook = useCustomHook();
  
  // 2. Event handlers
  const handleClick = () => { };
  const handleChange = () => { };
  
  // 3. Effects
  useEffect(() => { }, []);
  
  // 4. Computed values
  const computedValue = useMemo(() => { }, []);
  
  // 5. Render helpers (if needed)
  const renderItem = () => <div />;
  
  // 6. Return JSX
  return <div>...</div>;
}

Styling

Tailwind CSS

Use utility classes:

<div className="flex items-center justify-between p-4 bg-card rounded-lg">
  <h2 className="text-xl font-bold">Title</h2>
  <Button className="ml-auto">Action</Button>
</div>

Group related utilities:

// Layout
className="flex flex-col gap-4"

// Spacing
className="p-4 mx-auto"

// Typography
className="text-lg font-semibold text-foreground"

// Colors
className="bg-primary text-primary-foreground"

Use CSS variables:

className="bg-background text-foreground"
// Not: className="bg-white text-black"

Avoid Inline Styles

Bad:

<div style={{ padding: '16px', color: 'red' }}>

Good:

<div className="p-4 text-destructive">

Comments

When to Comment

Good reasons:

  • Explain why, not what
  • Complex algorithms
  • Non-obvious behavior
  • Workarounds
  • TODOs

Good:

// Convert UTC timestamp to local time for display
const localTime = new Date(utcTimestamp).toLocaleString();

// Workaround: D1 doesn't support DEFAULT with functions
// We remove DEFAULT clauses during migration
const sanitizedSQL = removeFunctionDefaults(sql);

Bad:

// Create a new date
const now = new Date();

// Loop through items
for (const item of items) { }

JSDoc Comments

For public APIs:

/**
 * Executes a SQL query on the specified database.
 * 
 * @param dbId - Database UUID
 * @param query - SQL query string
 * @param skipValidation - Skip dangerous query validation
 * @returns Query results with metadata
 * @throws Error if query execution fails
 */
async function executeQuery(
  dbId: string,
  query: string,
  skipValidation: boolean = false
): Promise<QueryResult> {
  // ...
}

TODO Comments

Format:

// TODO: Add pagination support
// TODO(username): Optimize this query
// FIXME: Race condition when updating state
// HACK: Workaround for Cloudflare API limitation

Error Handling

Try-Catch

Handle errors gracefully:

async function loadData() {
  try {
    const data = await api.fetchData();
    setData(data);
  } catch (error) {
    console.error('Failed to load data:', error);
    setError('Could not load data. Please try again.');
  } finally {
    setLoading(false);
  }
}

Error Messages

User-friendly messages:

Good:

setError('Could not load databases. Please check your connection.');

Bad:

setError(error.toString());  // Technical error message

Error Logging

Log for debugging:

try {
  // Operation
} catch (error) {
  console.error('[DatabaseView] Load failed:', error);
  // User-friendly message
  setError('Failed to load database');
}

Async/Await

Prefer async/await over promises:

Good:

async function loadData() {
  const data = await api.fetchData();
  return data;
}

Bad:

function loadData() {
  return api.fetchData().then(data => {
    return data;
  });
}

Handle errors:

async function loadData() {
  try {
    const data = await api.fetchData();
    return data;
  } catch (error) {
    handleError(error);
  }
}

Performance

Memoization

Use useMemo for expensive calculations:

const filteredItems = useMemo(
  () => items.filter(item => item.name.includes(search)),
  [items, search]
);

Use useCallback for event handlers:

const handleClick = useCallback(() => {
  // Handler logic
}, [dependencies]);

Avoid Unnecessary Re-renders

Use React.memo for pure components:

const TableRow = React.memo(function TableRow({ data }: Props) {
  return <tr>...</tr>;
});

Testing

Write testable code:

// ✅ Testable: Pure function
function calculateTotal(items: Item[]): number {
  return items.reduce((sum, item) => sum + item.price, 0);
}

// ❌ Hard to test: Side effects
function updateAndSave() {
  const data = fetchData();
  updateUI(data);
  saveToLocalStorage(data);
}

Security

Input Validation

Always validate user input:

function isValidDatabaseName(name: string): boolean {
  return /^[a-z0-9-]{3,63}$/.test(name) 
    && !name.startsWith('-') 
    && !name.endsWith('-');
}

if (!isValidDatabaseName(userInput)) {
  throw new Error('Invalid database name');
}

SQL Injection

Use parameterized queries:

// ✅ Safe
const result = await db.prepare(
  'SELECT * FROM users WHERE email = ?'
).bind(email).all();

// ❌ Unsafe
const query = `SELECT * FROM users WHERE email = '${email}'`;

Sensitive Data

Never log secrets:

// ❌ Bad
console.log('API Key:', env.API_KEY);

// ✅ Good
console.log('API call successful');

Linting

Run ESLint:

npm run lint

Fix automatically:

npm run lint -- --fix

No warnings in production code.

Next Steps


Write clean, maintainable code!

Clone this wiki locally