跳到主要内容

T1: Database Schema

Define and implement Drizzle ORM schema for PostgreSQL.


Overview​

AttributeValue
Task IDT1
DependenciesT0 (Project Setup)
Effort3 points
PriorityP0 (Critical Path)

Objectives​

  1. Define all database tables using Drizzle ORM
  2. Set up database client
  3. Create initial migration
  4. Test database connectivity

Deliverables​

1. Schema File (apps/web/lib/db/schema.ts)​

Full schema as defined in platform-upgrade-v2.md:

Tables to implement:

  • users - User accounts
  • apiKeys - API key management
  • oauthConnections - OAuth provider links
  • organizations - Team accounts
  • orgMembers - Organization membership
  • mcpPackages - MCP packages
  • skillPackages - Skill packages
  • collections - User collections
  • collectionItems - Collection contents
  • comments - Comments on entities
  • favorites - User favorites
  • ratings - User ratings
  • packageReleases - Version releases
  • downloadRecords - Download analytics
  • workspaces - User workspaces
  • workspaceEntities - Workspace configuration

2. Database Client (apps/web/lib/db/index.ts)​

import { drizzle } from 'drizzle-orm/postgres-js';
import postgres from 'postgres';
import * as schema from './schema';

const connectionString = process.env.POSTGRES_URL!;

// Connection for queries
const queryClient = postgres(connectionString);

// Connection for migrations (single connection)
const migrationClient = postgres(connectionString, { max: 1 });

export const db = drizzle(queryClient, { schema });
export const migrationDb = drizzle(migrationClient, { schema });

export * from './schema';

3. Drizzle Config (apps/web/drizzle.config.ts)​

import { defineConfig } from 'drizzle-kit';

export default defineConfig({
schema: './lib/db/schema.ts',
out: './lib/db/migrations',
dialect: 'postgresql',
dbCredentials: {
url: process.env.POSTGRES_URL!,
},
});

4. Type Exports (apps/web/lib/db/types.ts)​

import type { InferSelectModel, InferInsertModel } from 'drizzle-orm';
import * as schema from './schema';

// Select types (for reading)
export type User = InferSelectModel<typeof schema.users>;
export type ApiKey = InferSelectModel<typeof schema.apiKeys>;
export type McpPackage = InferSelectModel<typeof schema.mcpPackages>;
export type SkillPackage = InferSelectModel<typeof schema.skillPackages>;
export type PackageRelease = InferSelectModel<typeof schema.packageReleases>;
export type Collection = InferSelectModel<typeof schema.collections>;
export type Workspace = InferSelectModel<typeof schema.workspaces>;

// Insert types (for creating)
export type NewUser = InferInsertModel<typeof schema.users>;
export type NewApiKey = InferInsertModel<typeof schema.apiKeys>;
export type NewMcpPackage = InferInsertModel<typeof schema.mcpPackages>;
export type NewSkillPackage = InferInsertModel<typeof schema.skillPackages>;
export type NewPackageRelease = InferInsertModel<typeof schema.packageReleases>;
export type NewCollection = InferInsertModel<typeof schema.collections>;
export type NewWorkspace = InferInsertModel<typeof schema.workspaces>;

Schema Design Principles​

1. Use Text IDs (UUIDs)​

id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),

2. Timestamps​

createdAt: timestamp('created_at').defaultNow(),
updatedAt: timestamp('updated_at').defaultNow().$onUpdate(() => new Date()),

3. JSON Fields with Types​

tags: json('tags').$type<string[]>().default([]),
config: json('config').$type<Record<string, unknown>>(),

4. Cascading Deletes​

userId: text('user_id')
.notNull()
.references(() => users.id, { onDelete: 'cascade' }),

Acceptance Criteria​

  • All tables defined in schema.ts
  • pnpm db:generate creates migration
  • pnpm db:push applies schema to database
  • pnpm db:studio opens Drizzle Studio
  • Types exported and usable
  • Relations properly defined

Commands​

# Generate migration
pnpm --filter @viben/web db:generate

# Push to database (dev)
pnpm --filter @viben/web db:push

# Open Drizzle Studio
pnpm --filter @viben/web db:studio

Testing​

// Test database connection
import { db, users } from '@/lib/db';

async function testConnection() {
const result = await db.select().from(users).limit(1);
console.log('Database connected:', result);
}

Notes​

  • Use Neon's serverless driver for edge compatibility
  • Schema matches platform-upgrade-v2.md exactly
  • Indexes defined inline in schema for performance