-
Notifications
You must be signed in to change notification settings - Fork 2
Code Style Guide
Coding standards and conventions for D1 Database Manager.
Consistent code style makes the codebase easier to read, maintain, and contribute to.
- Readability - Code is read more than written
- Consistency - Follow existing patterns
- Simplicity - Prefer simple over clever
- Type Safety - Use TypeScript strictly
- DRY - Don't Repeat Yourself
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 anyUse 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;
}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 };Use const enums when possible:
const enum Status {
Active = 'active',
Inactive = 'inactive'
}Or string literal unions:
type Status = 'active' | 'inactive' | 'pending';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>;
}
}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;
}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>;
}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>;
}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
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;camelCase with verb prefix:
function loadDatabases() { }
function createTable() { }
function handleClick() { }
function validateInput() { }PascalCase:
function DatabaseView() { }
function TableGrid() { }
function FilterBar() { }PascalCase:
interface Database { }
type QueryResult = { };
interface TableViewProps { }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
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';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>;
}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"❌ Bad:
<div style={{ padding: '16px', color: 'red' }}>✅ Good:
<div className="p-4 text-destructive">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) { }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> {
// ...
}Format:
// TODO: Add pagination support
// TODO(username): Optimize this query
// FIXME: Race condition when updating state
// HACK: Workaround for Cloudflare API limitationHandle 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);
}
}User-friendly messages:
✅ Good:
setError('Could not load databases. Please check your connection.');❌ Bad:
setError(error.toString()); // Technical error messageLog for debugging:
try {
// Operation
} catch (error) {
console.error('[DatabaseView] Load failed:', error);
// User-friendly message
setError('Failed to load database');
}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);
}
}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]);Use React.memo for pure components:
const TableRow = React.memo(function TableRow({ data }: Props) {
return <tr>...</tr>;
});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);
}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');
}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}'`;Never log secrets:
// ❌ Bad
console.log('API Key:', env.API_KEY);
// ✅ Good
console.log('API call successful');Run ESLint:
npm run lintFix automatically:
npm run lint -- --fixNo warnings in production code.
- Contributing Guide - How to contribute
- Development Workflow - Git workflow
- Testing Guide - Testing standards
- Local Development - Development setup
Write clean, maintainable code! ✨
- Database Management
- Table Operations
- Query Console
- Column Management
- Bulk Operations
- Job History
- Time Travel
- Foreign Key Navigation
- Foreign Key Visualizer
- Foreign Key Dependencies
- Circular Dependency Detector
- Cascade Impact Simulator
- Constraint Validator
- Index Analyzer
- Undo Rollback
- Schema Designer
- Row Level Filtering