270 lines
6.9 KiB
Markdown
270 lines
6.9 KiB
Markdown
# Database Schema: [Project Name]
|
|
|
|
**Database**: Cloudflare D1 (SQLite)
|
|
**Migrations**: `migrations/` directory
|
|
**ORM**: [Drizzle ORM / Raw SQL / None]
|
|
**Schema Version**: 1.0
|
|
**Last Updated**: [Date]
|
|
|
|
---
|
|
|
|
## Overview
|
|
|
|
This document defines the complete database schema including tables, relationships, indexes, and migrations.
|
|
|
|
**Design Principles**:
|
|
- Normalize data to reduce redundancy
|
|
- Index frequently queried columns
|
|
- Use foreign keys for referential integrity
|
|
- Include timestamps for audit trail
|
|
- Use INTEGER for IDs (SQLite auto-increment)
|
|
- Use TEXT for strings (SQLite doesn't enforce varchar limits)
|
|
- Use INTEGER for booleans (0 = false, 1 = true)
|
|
- Use INTEGER for timestamps (Unix epoch)
|
|
|
|
---
|
|
|
|
## Tables
|
|
|
|
### `users`
|
|
**Purpose**: User accounts and authentication data
|
|
|
|
| Column | Type | Constraints | Default | Notes |
|
|
|--------|------|-------------|---------|-------|
|
|
| `id` | INTEGER | PRIMARY KEY | AUTO | Auto-increment |
|
|
| `email` | TEXT | UNIQUE, NOT NULL | - | Login identifier |
|
|
| `clerk_id` | TEXT | UNIQUE, NOT NULL | - | Clerk user ID |
|
|
| `display_name` | TEXT | NULL | - | User's display name |
|
|
| `avatar_url` | TEXT | NULL | - | Profile picture URL (R2) |
|
|
| `created_at` | INTEGER | NOT NULL | - | Unix timestamp |
|
|
| `updated_at` | INTEGER | NOT NULL | - | Unix timestamp |
|
|
|
|
**Indexes**:
|
|
- `idx_users_email` on `email` (for login lookups)
|
|
- `idx_users_clerk_id` on `clerk_id` (for auth verification)
|
|
|
|
**Relationships**:
|
|
- One-to-many with `[related_table]`
|
|
|
|
**Notes**:
|
|
- `clerk_id` comes from Clerk authentication
|
|
- `avatar_url` points to R2 storage if user uploads custom avatar
|
|
- Emails are unique and used for account identification
|
|
|
|
---
|
|
|
|
### `[table_name]`
|
|
**Purpose**: [Description of what this table stores]
|
|
|
|
| Column | Type | Constraints | Default | Notes |
|
|
|--------|------|-------------|---------|-------|
|
|
| `id` | INTEGER | PRIMARY KEY | AUTO | Auto-increment |
|
|
| `user_id` | INTEGER | FOREIGN KEY, NOT NULL | - | References `users(id)` |
|
|
| `[field]` | [TYPE] | [CONSTRAINTS] | [DEFAULT] | [Notes] |
|
|
| `created_at` | INTEGER | NOT NULL | - | Unix timestamp |
|
|
| `updated_at` | INTEGER | NOT NULL | - | Unix timestamp |
|
|
|
|
**Indexes**:
|
|
- `idx_[table]_user_id` on `user_id` (for user-specific queries)
|
|
- `idx_[table]_[field]` on `[field]` (if frequently queried)
|
|
|
|
**Relationships**:
|
|
- Many-to-one with `users`
|
|
- [Other relationships]
|
|
|
|
**Notes**:
|
|
- [Important details about this table]
|
|
|
|
---
|
|
|
|
### `[junction_table]` (for many-to-many relationships)
|
|
**Purpose**: Links [table_a] and [table_b] in many-to-many relationship
|
|
|
|
| Column | Type | Constraints | Default | Notes |
|
|
|--------|------|-------------|---------|-------|
|
|
| `id` | INTEGER | PRIMARY KEY | AUTO | Auto-increment |
|
|
| `[table_a]_id` | INTEGER | FOREIGN KEY, NOT NULL | - | References `[table_a](id)` |
|
|
| `[table_b]_id` | INTEGER | FOREIGN KEY, NOT NULL | - | References `[table_b](id)` |
|
|
| `created_at` | INTEGER | NOT NULL | - | Unix timestamp |
|
|
|
|
**Indexes**:
|
|
- `idx_[junction]_[table_a]_id` on `[table_a]_id`
|
|
- `idx_[junction]_[table_b]_id` on `[table_b]_id`
|
|
- `idx_[junction]_composite` on `([table_a]_id, [table_b]_id)` UNIQUE
|
|
|
|
**Relationships**:
|
|
- Many-to-one with `[table_a]`
|
|
- Many-to-one with `[table_b]`
|
|
|
|
**Notes**:
|
|
- Composite unique index prevents duplicate associations
|
|
|
|
---
|
|
|
|
## Relationships Diagram
|
|
|
|
```
|
|
┌─────────────┐
|
|
│ users │
|
|
└──────┬──────┘
|
|
│ 1
|
|
│
|
|
│ N
|
|
┌──────┴──────────┐
|
|
│ [child_table] │
|
|
└─────────────────┘
|
|
|
|
[Add more relationships as needed]
|
|
```
|
|
|
|
---
|
|
|
|
## Migrations
|
|
|
|
### Migration 0001: Initial Schema
|
|
**File**: `migrations/0001_initial_schema.sql`
|
|
**Created**: [Date]
|
|
**Purpose**: Create initial tables for [core entities]
|
|
|
|
**Creates**:
|
|
- `users` table
|
|
- `[other_tables]` tables
|
|
|
|
**Indexes**:
|
|
- All primary indexes for frequently queried columns
|
|
|
|
**Run**:
|
|
```bash
|
|
npx wrangler d1 execute [DB_NAME] --local --file=migrations/0001_initial_schema.sql
|
|
npx wrangler d1 execute [DB_NAME] --remote --file=migrations/0001_initial_schema.sql
|
|
```
|
|
|
|
---
|
|
|
|
### Migration 0002: [Description]
|
|
**File**: `migrations/0002_[name].sql`
|
|
**Created**: [Date]
|
|
**Purpose**: [What this migration does]
|
|
|
|
**Changes**:
|
|
- Add `[column]` to `[table]`
|
|
- Create `[new_table]` table
|
|
- Add index `[index_name]`
|
|
|
|
**Run**:
|
|
```bash
|
|
npx wrangler d1 execute [DB_NAME] --local --file=migrations/0002_[name].sql
|
|
npx wrangler d1 execute [DB_NAME] --remote --file=migrations/0002_[name].sql
|
|
```
|
|
|
|
---
|
|
|
|
## Seed Data
|
|
|
|
For development and testing, seed the database with sample data.
|
|
|
|
**File**: `migrations/seed.sql` (run manually, not in production)
|
|
|
|
**Sample Data**:
|
|
- 3-5 test users
|
|
- [Other sample data relevant to testing]
|
|
|
|
**Run**:
|
|
```bash
|
|
npx wrangler d1 execute [DB_NAME] --local --file=migrations/seed.sql
|
|
```
|
|
|
|
**Sample Users**:
|
|
```sql
|
|
INSERT INTO users (email, clerk_id, display_name, created_at, updated_at)
|
|
VALUES
|
|
('test1@example.com', 'clerk_test_1', 'Test User 1', strftime('%s', 'now'), strftime('%s', 'now')),
|
|
('test2@example.com', 'clerk_test_2', 'Test User 2', strftime('%s', 'now'), strftime('%s', 'now')),
|
|
('test3@example.com', 'clerk_test_3', 'Test User 3', strftime('%s', 'now'), strftime('%s', 'now'));
|
|
```
|
|
|
|
---
|
|
|
|
## Query Patterns
|
|
|
|
### Common Queries
|
|
|
|
**Get user by email**:
|
|
```sql
|
|
SELECT * FROM users WHERE email = ?;
|
|
```
|
|
|
|
**Get all [items] for a user**:
|
|
```sql
|
|
SELECT * FROM [table] WHERE user_id = ? ORDER BY created_at DESC;
|
|
```
|
|
|
|
**Get [item] with related data**:
|
|
```sql
|
|
SELECT
|
|
[table].*,
|
|
users.display_name,
|
|
users.avatar_url
|
|
FROM [table]
|
|
JOIN users ON [table].user_id = users.id
|
|
WHERE [table].id = ?;
|
|
```
|
|
|
|
---
|
|
|
|
## Constraints and Validation
|
|
|
|
### Enforced at Database Level
|
|
- Primary keys (unique, not null)
|
|
- Foreign keys (referential integrity)
|
|
- Unique constraints (email, composite indexes)
|
|
- Not null constraints (required fields)
|
|
|
|
### Enforced at Application Level (Zod)
|
|
- Email format validation
|
|
- String length limits
|
|
- Enum values
|
|
- Complex business logic
|
|
|
|
**Why split?**: Database enforces data integrity, application provides user-friendly error messages.
|
|
|
|
---
|
|
|
|
## Backup and Restore
|
|
|
|
### Export Database
|
|
```bash
|
|
npx wrangler d1 export [DB_NAME] --local --output=backup.sql
|
|
npx wrangler d1 export [DB_NAME] --remote --output=backup.sql
|
|
```
|
|
|
|
### Import Database
|
|
```bash
|
|
npx wrangler d1 execute [DB_NAME] --local --file=backup.sql
|
|
npx wrangler d1 execute [DB_NAME] --remote --file=backup.sql
|
|
```
|
|
|
|
---
|
|
|
|
## Performance Considerations
|
|
|
|
**Indexes**: All frequently queried columns are indexed
|
|
**Query Optimization**: Use `EXPLAIN QUERY PLAN` to check query performance
|
|
**Pagination**: Use `LIMIT` and `OFFSET` for large result sets
|
|
**Caching**: Consider Workers KV for frequently accessed, rarely changed data
|
|
|
|
---
|
|
|
|
## Future Enhancements
|
|
|
|
Potential schema changes to consider:
|
|
- [ ] [Feature requiring schema change]
|
|
- [ ] [Another potential enhancement]
|
|
|
|
---
|
|
|
|
## Revision History
|
|
|
|
**v1.0** ([Date]): Initial schema design
|
|
**v1.1** ([Date]): [Changes made]
|