Convert database schemas from architecture blueprints to production-ready ORM code (Prisma, Drizzle, TypeORM). Includes all tables, relationships, indexes, constraints, and Row-Level Security policies.
Generates production-ready ORM code and migrations from database schema blueprints with security policies and seed data.
npx claudepluginhub navraj007in/architecture-cowork-pluginThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Convert your architecture blueprint's database schema into production-ready ORM code with migrations, indexes, and security policies.
Perfect for: Project setup, database initialization, schema-first development, team synchronization
Use this skill when you need to:
Input: Architecture blueprint (Section 4: Database Schema) Output: ORM schema files, migration files, seed data
Output: prisma/schema.prisma, prisma/migrations/
Why Prisma:
Example Output:
// prisma/schema.prisma
generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
model Tenant {
id String @id @default(cuid())
name String
domain String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relationships
users User[]
tickets Ticket[]
@@index([domain])
@@map("tenants")
}
model User {
id String @id @default(cuid())
tenantId String
email String
name String?
role UserRole @default(AGENT)
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relationships
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
tickets Ticket[] @relation("AssignedTickets")
@@unique([tenantId, email])
@@index([tenantId])
@@index([email])
@@map("users")
}
enum UserRole {
ADMIN
AGENT
VIEWER
}
model Ticket {
id String @id @default(cuid())
tenantId String
title String
description String @db.Text
status TicketStatus @default(OPEN)
priority Priority @default(MEDIUM)
assigneeId String?
createdBy String
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
// Relationships
tenant Tenant @relation(fields: [tenantId], references: [id], onDelete: Cascade)
assignee User? @relation("AssignedTickets", fields: [assigneeId], references: [id])
comments Comment[]
@@index([tenantId, status])
@@index([assigneeId])
@@index([createdAt])
@@map("tickets")
}
enum TicketStatus {
OPEN
IN_PROGRESS
RESOLVED
CLOSED
}
enum Priority {
LOW
MEDIUM
HIGH
URGENT
}
Output: db/schema.ts, db/migrations/
Why Drizzle:
Example Output:
// db/schema.ts
import { pgTable, text, timestamp, pgEnum, index, uniqueIndex } from 'drizzle-orm/pg-core';
import { relations } from 'drizzle-orm';
export const userRoleEnum = pgEnum('user_role', ['ADMIN', 'AGENT', 'VIEWER']);
export const ticketStatusEnum = pgEnum('ticket_status', ['OPEN', 'IN_PROGRESS', 'RESOLVED', 'CLOSED']);
export const priorityEnum = pgEnum('priority', ['LOW', 'MEDIUM', 'HIGH', 'URGENT']);
export const tenants = pgTable('tenants', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
name: text('name').notNull(),
domain: text('domain').notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
}, (table) => ({
domainIdx: uniqueIndex('tenants_domain_idx').on(table.domain),
}));
export const users = pgTable('users', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
tenantId: text('tenant_id').notNull().references(() => tenants.id, { onDelete: 'cascade' }),
email: text('email').notNull(),
name: text('name'),
role: userRoleEnum('role').default('AGENT').notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
}, (table) => ({
tenantEmailIdx: uniqueIndex('users_tenant_email_idx').on(table.tenantId, table.email),
tenantIdx: index('users_tenant_idx').on(table.tenantId),
emailIdx: index('users_email_idx').on(table.email),
}));
export const tickets = pgTable('tickets', {
id: text('id').primaryKey().$defaultFn(() => crypto.randomUUID()),
tenantId: text('tenant_id').notNull().references(() => tenants.id, { onDelete: 'cascade' }),
title: text('title').notNull(),
description: text('description').notNull(),
status: ticketStatusEnum('status').default('OPEN').notNull(),
priority: priorityEnum('priority').default('MEDIUM').notNull(),
assigneeId: text('assignee_id').references(() => users.id),
createdBy: text('created_by').notNull(),
createdAt: timestamp('created_at').defaultNow().notNull(),
updatedAt: timestamp('updated_at').defaultNow().notNull(),
}, (table) => ({
tenantStatusIdx: index('tickets_tenant_status_idx').on(table.tenantId, table.status),
assigneeIdx: index('tickets_assignee_idx').on(table.assigneeId),
createdAtIdx: index('tickets_created_at_idx').on(table.createdAt),
}));
// Relations
export const tenantsRelations = relations(tenants, ({ many }) => ({
users: many(users),
tickets: many(tickets),
}));
export const usersRelations = relations(users, ({ one, many }) => ({
tenant: one(tenants, { fields: [users.tenantId], references: [tenants.id] }),
assignedTickets: many(tickets),
}));
export const ticketsRelations = relations(tickets, ({ one, many }) => ({
tenant: one(tenants, { fields: [tickets.tenantId], references: [tenants.id] }),
assignee: one(users, { fields: [tickets.assigneeId], references: [users.id] }),
comments: many(comments),
}));
Output: src/entities/, src/migrations/
Why TypeORM:
Output: db/migrations/001_initial_schema.sql
Why SQL:
Extract from Section 4: Database Schema:
## Database Schema
### Entities
#### Tenant
- `id` (UUID, PK)
- `name` (String, required)
- `domain` (String, unique, required)
- `created_at` (Timestamp)
- `updated_at` (Timestamp)
**Relationships**:
- Has many Users
- Has many Tickets
**Indexes**:
- `domain` (unique)
#### User
- `id` (UUID, PK)
- `tenant_id` (UUID, FK ā Tenant, required)
- `email` (String, required)
- `name` (String, optional)
- `role` (Enum: ADMIN, AGENT, VIEWER, default: AGENT)
- `created_at` (Timestamp)
- `updated_at` (Timestamp)
**Relationships**:
- Belongs to Tenant
- Has many Tickets (as assignee)
**Indexes**:
- `(tenant_id, email)` (unique composite)
- `tenant_id`
- `email`
**Constraints**:
- ON DELETE CASCADE (when tenant deleted, delete users)
Check Section 3: Tech Stack Decisions for ORM choice:
For each entity:
Map data types: Blueprint types ā ORM types
UUID ā @id @default(cuid()) (Prisma) or text('id').primaryKey() (Drizzle)String ā String or text()Timestamp ā DateTime or timestamp()Enum ā enum or pgEnum()Add relationships: Foreign keys, one-to-many, many-to-many
tenant_id FK ā Tenant ā @relation(fields: [tenantId], references: [id])Add indexes: Performance optimization
@@index([column])@@index([col1, col2])@@unique([column])Add constraints: Cascades, checks, defaults
onDelete: Cascade for multi-tenant isolation@default(now()) for timestamps@default(AGENT) for enumsIf multi-tenant B2B SaaS detected, add RLS:
PostgreSQL RLS (SQL):
-- Enable RLS on all tables
ALTER TABLE tenants ENABLE ROW LEVEL SECURITY;
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE tickets ENABLE ROW LEVEL SECURITY;
-- Policy: Users can only see their own tenant's data
CREATE POLICY tenant_isolation_policy ON users
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
CREATE POLICY tenant_isolation_policy ON tickets
FOR ALL
USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
-- Admins can see all tenants (override policy)
CREATE POLICY admin_override_policy ON users
FOR ALL
TO admin_role
USING (true);
Prisma/Drizzle Middleware (app-level RLS):
// prisma/middleware.ts
import { Prisma } from '@prisma/client';
export function tenantMiddleware(tenantId: string) {
return Prisma.defineExtension((client) => {
return client.$extends({
query: {
$allModels: {
async $allOperations({ args, query, model }) {
// Automatically add tenantId filter to all queries
if ('where' in args) {
args.where = { ...args.where, tenantId };
}
if ('data' in args && model !== 'Tenant') {
args.data = { ...args.data, tenantId };
}
return query(args);
},
},
},
});
});
}
// Usage:
const db = prisma.$extends(tenantMiddleware(currentTenantId));
Create prisma/seed.ts or db/seed.ts:
// prisma/seed.ts
import { PrismaClient } from '@prisma/client';
const prisma = new PrismaClient();
async function main() {
// Create demo tenant
const tenant = await prisma.tenant.create({
data: {
name: 'Acme Corp',
domain: 'acme.example.com',
},
});
// Create admin user
const admin = await prisma.user.create({
data: {
tenantId: tenant.id,
email: 'admin@acme.com',
name: 'Admin User',
role: 'ADMIN',
},
});
// Create sample ticket
await prisma.ticket.create({
data: {
tenantId: tenant.id,
title: 'Welcome to the system',
description: 'This is a sample ticket',
status: 'OPEN',
priority: 'LOW',
createdBy: admin.id,
},
});
console.log('ā
Database seeded successfully');
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
Add to package.json:
{
"scripts": {
"db:seed": "tsx prisma/seed.ts"
},
"prisma": {
"seed": "tsx prisma/seed.ts"
}
}
Prisma:
npx prisma migrate dev --name init
# Creates: prisma/migrations/20260207000000_init/migration.sql
Drizzle:
npx drizzle-kit generate:pg
# Creates: db/migrations/0000_initial_schema.sql
Raw SQL:
-- db/migrations/001_initial_schema.sql
-- Create enums
CREATE TYPE user_role AS ENUM ('ADMIN', 'AGENT', 'VIEWER');
CREATE TYPE ticket_status AS ENUM ('OPEN', 'IN_PROGRESS', 'RESOLVED', 'CLOSED');
CREATE TYPE priority AS ENUM ('LOW', 'MEDIUM', 'HIGH', 'URGENT');
-- Create tables
CREATE TABLE tenants (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
domain TEXT NOT NULL UNIQUE,
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);
CREATE INDEX tenants_domain_idx ON tenants(domain);
CREATE TABLE users (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
tenant_id UUID NOT NULL REFERENCES tenants(id) ON DELETE CASCADE,
email TEXT NOT NULL,
name TEXT,
role user_role NOT NULL DEFAULT 'AGENT',
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
UNIQUE(tenant_id, email)
);
CREATE INDEX users_tenant_idx ON users(tenant_id);
CREATE INDEX users_email_idx ON users(email);
[... more tables ...]
-- Enable Row-Level Security
ALTER TABLE tenants ENABLE ROW LEVEL SECURITY;
ALTER TABLE users ENABLE ROW LEVEL SECURITY;
ALTER TABLE tickets ENABLE ROW LEVEL SECURITY;
-- RLS Policies
CREATE POLICY tenant_isolation_policy ON users
FOR ALL USING (tenant_id = current_setting('app.current_tenant_id')::uuid);
[... more policies ...]
When invoked, generate:
šļø Generating data model from blueprint...
ā
Detected ORM: Prisma (from Next.js tech stack)
ā
Parsed database schema (5 entities, 12 relationships)
- Entities: Tenant, User, Ticket, Comment, Attachment
- Relationships: 8 one-to-many, 4 many-to-many
- Indexes: 15 (performance optimized)
- Constraints: 8 cascade deletes
ā
Generated prisma/schema.prisma (287 lines)
ā
Generated prisma/seed.ts (sample data)
ā
Generated prisma/middleware.ts (RLS for multi-tenancy)
ā
Generated db/rls-policies.sql (PostgreSQL RLS)
š¦ Next steps to initialize database:
1. Install Prisma:
npm install prisma @prisma/client
npm install -D tsx
2. Generate Prisma Client:
npx prisma generate
3. Run initial migration:
npx prisma migrate dev --name init
4. Seed database with sample data:
npm run db:seed
5. Open Prisma Studio (database GUI):
npx prisma studio
š Multi-tenancy configured:
- All queries automatically scoped by tenant_id
- Row-Level Security policies enabled
- Cascade deletes configured
- Sample middleware for app-level isolation
Prisma:
// Auto-generated types from schema
import { PrismaClient, User, Ticket, TicketStatus } from '@prisma/client';
const db = new PrismaClient();
// Type-safe queries
const user: User = await db.user.findUnique({
where: { id: '123' },
include: { tickets: true }, // Type-safe includes
});
// Type-safe creates
const ticket: Ticket = await db.ticket.create({
data: {
tenantId: 'tenant-123',
title: 'New ticket',
status: TicketStatus.OPEN, // Enum type
priority: 'HIGH', // Type error if invalid
},
});
Automatically add indexes for:
Version control for schema changes:
prisma/migrations/
āāā 20260207_init/
ā āāā migration.sql
āāā 20260214_add_priority/
ā āāā migration.sql
āāā 20260221_add_attachments/
āāā migration.sql
Rollback support:
# Rollback last migration
npx prisma migrate resolve --rolled-back 20260221_add_attachments
# Reset database (dev only)
npx prisma migrate reset
Generate realistic seed data based on product type:
/architect:blueprint first."// Assuming one-to-many: Tenant has many UsersA successful data model generation should:
/architect:generate-data-model
# Output:
# ā
Generated prisma/schema.prisma
# ā
Generated prisma/seed.ts
# ā
Generated prisma/middleware.ts (RLS)
/architect:generate-data-model --orm=drizzle
# Output:
# ā
Generated db/schema.ts
# ā
Generated db/seed.ts
# ā
Generated drizzle.config.ts
/architect:generate-data-model --orm=sql
# Output:
# ā
Generated db/migrations/001_initial_schema.sql
# ā
Generated db/seed.sql
# ā
Generated db/rls-policies.sql
/architect:generate-data-model --seed
# Output:
# ā
Generated schema + seed data
# ā
Created 3 demo tenants, 10 users, 25 tickets
Expert guidance for Next.js Cache Components and Partial Prerendering (PPR). **PROACTIVE ACTIVATION**: Use this skill automatically when working in Next.js projects that have `cacheComponents: true` in their next.config.ts/next.config.js. When this config is detected, proactively apply Cache Components patterns and best practices to all React Server Component implementations. **DETECTION**: At the start of a session in a Next.js project, check for `cacheComponents: true` in next.config. If enabled, this skill's patterns should guide all component authoring, data fetching, and caching decisions. **USE CASES**: Implementing 'use cache' directive, configuring cache lifetimes with cacheLife(), tagging cached data with cacheTag(), invalidating caches with updateTag()/revalidateTag(), optimizing static vs dynamic content boundaries, debugging cache issues, and reviewing Cache Component implementations.
Applies Anthropic's official brand colors and typography to any sort of artifact that may benefit from having Anthropic's look-and-feel. Use it when brand colors or style guidelines, visual formatting, or company design standards apply.
Creating algorithmic art using p5.js with seeded randomness and interactive parameter exploration. Use this when users request creating art using code, generative art, algorithmic art, flow fields, or particle systems. Create original algorithmic art rather than copying existing artists' work to avoid copyright violations.