This commit is contained in:
2026-02-16 19:34:35 +00:00
commit 9ab0940ca4
784 changed files with 41710 additions and 0 deletions
+100
View File
@@ -0,0 +1,100 @@
# Database Patterns
## Overview
Three PostgreSQL databases managed with Drizzle ORM:
| Database | Purpose | Package |
|----------|---------|---------|
| `officer_db` | Main app data | `officerdb` |
| `ephemeral_db` | Cache & temporary data | `ephemeraldb` |
## Type System
### File Structure
Each database package has:
```
db_name/
├── src/
│ ├── index.ts # DB connection, exports schema + drizzle helpers
│ ├── types.ts # All type exports (Select, Insert, extended)
│ └── schema/
│ ├── index.ts # Re-exports all schema files
│ └── *.ts # Table definitions
└── package.json # Exports: "." and "./types"
```
### Type Naming Convention
```ts
// Pattern 1: Simple table (no relations needed in API)
export type Screenshot = typeof Schema.Screenshots.$inferSelect;
export type ScreenshotInsert = typeof Schema.Screenshots.$inferInsert;
// Pattern 2: Table with relations (for hydrated API responses)
export type UserSelect = typeof Schema.Users.$inferSelect;
export type UserInsert = typeof Schema.Users.$inferInsert;
export type User = UserSelect & {
company: Company;
passkeys: Passkey[];
// computed fields
passkeyCount: number;
};
```
### Type Organization
Organize types by domain with comments:
```ts
// officerdb/types.ts
// Auth
export type PasskeySelect = ...
export type Passkey = PasskeySelect & { user: User };
// Companies & Websites
export type CompanySelect = ...
export type Company = CompanySelect & { ... };
// Experiments
export type ExperimentSelect = ...
export type Experiment = ExperimentSelect & { ... };
```
## Importing Types
```ts
// ✅ Good - import from types subpath
import type { User } from 'officerdb/types';
// ✅ Good - import schema/connection from main
import { officerdb, eq, Users } from 'officerdb';
// ❌ Bad - don't define manual types in schema files
// ❌ Bad - don't import types from schema directly
```
## Null Safety
Drizzle-inferred types correctly reflect nullable columns. Add guards when needed:
```ts
export async function handleOauthAccount(account: GanOauth) {
// Guard for nullable fields
if (!account.refreshToken || !account.companyId) {
return;
}
// Now TypeScript knows these are non-null
const token = await refreshAccessToken(account.refreshToken);
}
```
## Schema Best Practices
- Use `bigserial` with `mode: 'number'` for IDs
- Use `bigint` with `mode: 'number'` for foreign keys
- Always add indexes for frequently queried columns
- Use `varchar` with explicit length limits
- Timestamps: `timestamp('...', { withTimezone: true })`