Initial commit

This commit is contained in:
Zhongwei Li
2025-11-29 18:29:10 +08:00
commit 657f1e3da3
29 changed files with 2738 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
# Database Convention Examples Index
**All files under 500 lines for optimal loading.**
- **[drizzle-schemas.md](drizzle-schemas.md)** - TypeScript/Drizzle ORM examples
- **[sqlmodel-schemas.md](sqlmodel-schemas.md)** - Python/SQLModel examples
- **[migrations.md](migrations.md)** - Migration patterns for both stacks
- **[rls-policies.md](rls-policies.md)** - Row Level Security examples

View File

@@ -0,0 +1,87 @@
# Drizzle Schema Examples
**Complete TypeScript/Drizzle ORM schema patterns.**
## Basic Table with Multi-Tenant
```typescript
// db/schema/users.ts
import { pgTable, uuid, text, timestamp, boolean, index } from "drizzle-orm/pg-core";
export const users = pgTable("users", {
// Primary key
id: uuid("id").primaryKey().defaultRandom(),
// Timestamps (required on all tables)
created_at: timestamp("created_at").defaultNow().notNull(),
updated_at: timestamp("updated_at").defaultNow().notNull().$onUpdate(() => new Date()),
// Multi-tenant (required on all tables)
tenant_id: uuid("tenant_id").notNull(),
// User fields
email_address: text("email_address").notNull().unique(),
full_name: text("full_name").notNull(),
hashed_password: text("hashed_password").notNull(),
is_active: boolean("is_active").default(true).notNull(),
is_verified: boolean("is_verified").default(false).notNull(),
last_login_at: timestamp("last_login_at"),
// Soft delete
deleted_at: timestamp("deleted_at"),
});
// Indexes
export const usersIndex = index("users_tenant_id_idx").on(users.tenant_id);
export const usersEmailIndex = index("users_email_idx").on(users.email_address);
```
## Reusable Timestamp Pattern
```typescript
// db/schema/base.ts
export const baseTimestamps = {
created_at: timestamp("created_at").defaultNow().notNull(),
updated_at: timestamp("updated_at").defaultNow().notNull().$onUpdate(() => new Date()),
};
// Use in tables
export const teams = pgTable("teams", {
id: uuid("id").primaryKey().defaultRandom(),
...baseTimestamps,
tenant_id: uuid("tenant_id").notNull(),
name: text("name").notNull(),
});
```
## One-to-Many Relationships
```typescript
// db/schema/posts.ts
import { relations } from "drizzle-orm";
export const posts = pgTable("posts", {
id: uuid("id").primaryKey().defaultRandom(),
created_at: timestamp("created_at").defaultNow().notNull(),
updated_at: timestamp("updated_at").defaultNow().notNull(),
tenant_id: uuid("tenant_id").notNull(),
// Foreign key to users
author_id: uuid("author_id").notNull(),
title: text("title").notNull(),
content: text("content").notNull(),
is_published: boolean("is_published").default(false).notNull(),
});
// Define relations
export const usersRelations = relations(users, ({ many }) => ({
posts: many(posts), // User has many posts
}));
export const postsRelations = relations(posts, ({ one }) => ({
author: one(users, { fields: [posts.author_id], references: [users.id] }),
}));
```
**See [../templates/drizzle-table.ts](../templates/drizzle-table.ts) for complete template.**

View File

@@ -0,0 +1,31 @@
# Migration Examples
**Migration patterns for Drizzle (TypeScript) and Alembic (Python).**
## Drizzle Migrations (TypeScript)
```bash
# Generate migration
bun run drizzle-kit generate:pg
# Apply migration
bun run drizzle-kit push:pg
# Check migration status
bun run drizzle-kit check:pg
```
## Alembic Migrations (Python)
```bash
# Generate migration
alembic revision --autogenerate -m "Add users table"
# Apply migration
alembic upgrade head
# Rollback
alembic downgrade -1
```
**See [../reference/migrations.md](../reference/migrations.md) for complete setup.**

View File

@@ -0,0 +1,27 @@
# Row Level Security Examples
**RLS policy patterns for multi-tenant isolation.**
## Enable RLS
```sql
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
```
## Tenant Isolation Policy
```sql
CREATE POLICY "tenant_isolation" ON users
FOR ALL TO authenticated
USING (tenant_id = (current_setting('request.jwt.claims')::json->>'tenant_id')::uuid);
```
## Admin Override Policy
```sql
CREATE POLICY "admin_access" ON users
FOR ALL TO admin
USING (true);
```
**See [../reference/rls-policies.md](../reference/rls-policies.md) for complete RLS guide.**

View File

@@ -0,0 +1,102 @@
# SQLModel Schema Examples
**Complete Python/SQLModel schema patterns.**
## Basic Model with Multi-Tenant
```python
# app/models/user.py
from sqlmodel import Field, SQLModel
from uuid import UUID, uuid4
from datetime import datetime
from typing import Optional
class User(SQLModel, table=True):
"""User model with multi-tenant isolation."""
__tablename__ = "users"
# Primary key
id: UUID = Field(default_factory=uuid4, primary_key=True)
# Timestamps (required on all tables)
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(
default_factory=datetime.utcnow,
sa_column_kwargs={"onupdate": datetime.utcnow}
)
# Multi-tenant (required on all tables)
tenant_id: UUID = Field(foreign_key="tenants.id", index=True)
# User fields
email_address: str = Field(unique=True, index=True, max_length=255)
full_name: str = Field(max_length=255)
hashed_password: str = Field(max_length=255)
is_active: bool = Field(default=True)
is_verified: bool = Field(default=False)
last_login_at: Optional[datetime] = None
# Soft delete
deleted_at: Optional[datetime] = None
```
## Reusable Timestamp Mixin
```python
# app/models/base.py
from sqlmodel import Field
from datetime import datetime
class TimestampMixin:
"""Mixin for created_at and updated_at timestamps."""
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(
default_factory=datetime.utcnow,
sa_column_kwargs={"onupdate": datetime.utcnow}
)
# Use in models
class Team(TimestampMixin, SQLModel, table=True):
__tablename__ = "teams"
id: UUID = Field(default_factory=uuid4, primary_key=True)
tenant_id: UUID = Field(foreign_key="tenants.id", index=True)
name: str = Field(max_length=255)
```
## One-to-Many Relationships
```python
# app/models/post.py
from sqlmodel import Field, SQLModel, Relationship
from uuid import UUID, uuid4
from datetime import datetime
from typing import Optional, List
class Post(SQLModel, table=True):
"""Post model with author relationship."""
__tablename__ = "posts"
id: UUID = Field(default_factory=uuid4, primary_key=True)
created_at: datetime = Field(default_factory=datetime.utcnow)
updated_at: datetime = Field(default_factory=datetime.utcnow)
tenant_id: UUID = Field(foreign_key="tenants.id", index=True)
# Foreign key to users
author_id: UUID = Field(foreign_key="users.id", index=True)
title: str = Field(max_length=255)
content: str
is_published: bool = Field(default=False)
# Relationship
author: Optional["User"] = Relationship(back_populates="posts")
# Add to User model
class User(TimestampMixin, SQLModel, table=True):
# ... (previous fields)
posts: List["Post"] = Relationship(back_populates="author")
```
**See [../templates/sqlmodel-table.py](../templates/sqlmodel-table.py) for complete template.**