From core-claude-plugin
Use when scaffolding, auditing, or validating Prisma database packages in MetaSaver monorepos. Covers package structure, Prisma schema setup, database client initialization, and seed scripts. File types: .prisma, .ts, package.json.
npx claudepluginhub metasaver/metasaver-marketplace --plugin core-claude-pluginThis skill is limited to using the following tools:
This skill documents the complete file and folder organization for MetaSaver Prisma database packages (e.g., rugby-crm-database). It ensures consistent patterns across:
Provides expert guidance on Prisma ORM for TypeScript apps: schema design, migrations, Prisma Client queries, relations, edge deployment, and performance optimization.
Assists with Prisma schema helper operations: generates configurations, code, and best practices for backend database tasks in Node.js, Python, or Go. Auto-activates on 'prisma schema helper' mentions.
Provides expert guidance on Prisma ORM schema design, migrations, query optimization, relations modeling, and database operations for PostgreSQL, MySQL, and SQLite.
Share bugs, ideas, or general feedback.
This skill documents the complete file and folder organization for MetaSaver Prisma database packages (e.g., rugby-crm-database). It ensures consistent patterns across:
Use when:
packages/database/{project}-database/
├── package.json # Package metadata and scripts
├── tsconfig.json # TypeScript configuration
├── README.md # Package documentation
├── .env.example # Environment variable template
├── eslint.config.js # ESLint configuration (flat config)
└── dist/ # Build output (generated)
prisma/
├── schema.prisma # Database schema definition
├── seed/
│ ├── index.ts # Main seed entry point
│ └── {entity}.ts # Entity-specific seed data
└── migrations/ # Database migrations (generated)
└── {timestamp}_{name}/ # Migration folder (auto-generated)
└── migration.sql # Migration SQL
src/
├── index.ts # Barrel export (main API)
├── client.ts # Prisma client singleton
└── types.ts # Type re-exports from Prisma
Complete Example:
packages/database/rugby-crm-database/
├── package.json
├── tsconfig.json
├── eslint.config.js
├── README.md
├── .env.example
├── prisma/
│ ├── schema.prisma
│ ├── seed/
│ │ ├── index.ts
│ │ └── user.ts
│ └── migrations/
│ └── [auto-generated by Prisma]
├── src/
│ ├── index.ts
│ ├── client.ts
│ └── types.ts
└── dist/ # Build output (generated)
├── index.d.ts
├── index.js
└── [compiled files]
Required Fields:
name: @metasaver/{project}-database (always scoped)version: 0.1.0 (start with patch version)type: "module" (ESM packages)description: "{Description} database package"main: "./dist/index.js"types: "./dist/index.d.ts"exports: Proper export field for ESMRequired Scripts:
build: TypeScript compilation (with Prisma generate)clean: Remove build artifactsdb:generate: Prisma client generationdb:migrate: Deploy migrations to databasedb:migrate:dev: Dev migration with promptdb:seed: Run seed scriptsdb:studio: Open Prisma Studiodb:push: Push schema changes to database (dev only)lint, lint:fix, lint:tsc, prettier, prettier:fixtest:unit: Unit tests (stub or actual)Build Script Details:
"build": "dotenv -e ../../../.env -- prisma generate && tsc -b"
This command:
.envDependencies:
@prisma/client: ^6.16.2 (production)tsconfig.json Pattern:
{
"extends": "@metasaver/core-typescript-config/base",
"compilerOptions": {
"rootDir": "./src",
"outDir": "./dist"
},
"include": ["src/**/*"],
"exclude": ["node_modules", "dist", "**/*.test.ts", "prisma"]
}
Key Points:
rootDir: ./srcoutDir: ./distRule 1: Datasource Configuration
Provider: "postgresql" (standard for MetaSaver)
datasource db {
provider = "postgresql"
url = env("{PROJECT_UPPER}_DATABASE_URL")
}
generator client {
provider = "prisma-client-js"
}
Rule 2: Model Naming
User, Team)@@map() (e.g., @@map("users"))@map()model User {
id String @id @default(uuid())
email String
name String
status String @default("active")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("users")
}
Rule 3: Standard Fields
All models must have:
id: String @id @default(uuid()) - UUID primary keycreatedAt: DateTime @default(now()) @map("created_at") - Creation timestampupdatedAt: DateTime @updatedAt @map("updated_at") - Update timestampRule 4: Relations
Use foreign keys with proper cascade behavior:
model User {
id String @id @default(uuid())
name String
teams Team[]
@@map("users")
}
model Team {
id String @id @default(uuid())
userId String @map("user_id")
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@map("teams")
}
Pattern: Simple Singleton
File: src/client.ts
import pkg from "@prisma/client";
const { PrismaClient } = pkg;
declare global {
var prisma: InstanceType<typeof PrismaClient> | undefined;
}
export const prisma = global.prisma || new PrismaClient();
if (process.env.NODE_ENV !== "production") {
global.prisma = prisma;
}
export default prisma;
Key Points:
prismaRule: Simple Type Re-export
File: src/types.ts
export type * from "@prisma/client";
Key Points:
Rule 1: Seed Entry Point
File: prisma/seed/index.ts
// Import order example
import { prisma } from "#/client.js";
import { seedUsers } from "./users.js";
import { seedTeams } from "./teams.js";
async function seed() {
try {
await seedUsers(prisma);
await seedTeams(prisma);
console.log("Seed completed successfully");
} catch (error) {
console.error("Seed failed:", error);
process.exit(1);
} finally {
await prisma.$disconnect();
}
}
seed();
Import pattern: Use #/ alias for internal imports within database package.
Rule 2: Entity-Specific Seed Files
File: prisma/seed/{entity}.ts
import type { PrismaClient } from "@prisma/client";
export async function seedUsers(prisma: PrismaClient) {
const users = [
{ id: "user-1", email: "alice@example.com", name: "Alice" },
{ id: "user-2", email: "bob@example.com", name: "Bob" },
];
for (const user of users) {
await prisma.user.upsert({
where: { id: user.id },
update: { name: user.name },
create: user,
});
}
console.log(`Seeded ${users.length} users`);
}
Rule 3: Seed Data Idempotency
upsert pattern to prevent duplicates on re-runRule 1: Variable Naming
Format: {PROJECT_UPPER}_DATABASE_URL
Example for rugby-crm:
RUGBY_CRM_DATABASE_URL=postgresql://user:password@localhost:5432/rugby_crm
Rule 2: .env.example Template
# Database (required for build and migrations)
{PROJECT_UPPER}_DATABASE_URL=postgresql://user:password@localhost:5432/{project_lower}
Rule 3: Script Environment Usage
The build and migration scripts use dotenv-cli to load environment variables:
{
"scripts": {
"build": "dotenv -e ../../../.env -- prisma generate && tsc -b",
"db:migrate": "dotenv -e ../../../.env -- prisma migrate deploy"
}
}
Package exports field:
"exports": {
"./client": "./dist/client.js",
"./types": "./dist/types.js"
}
Consumer import examples:
// External package imports (from other workspace packages)
import { prisma } from "@metasaver/rugby-crm-database/client";
import type { User, Team } from "@metasaver/rugby-crm-database/types";
// Internal imports (within same package) - use #/ alias
import { prisma } from "#/client.js";
import type { User } from "#/types.js";
Key Points:
exports field for public API#/ aliasCreate Package Directory
mkdir -p packages/database/{project}-database/{src,prisma/seed}
Create Configuration Files (use templates)
package.jsontsconfig.json.env.exampleeslint.config.js (copy from existing package)Create Prisma Schema (use template)
prisma/schema.prismaCreate Database Client (use template)
src/client.tsCreate Types (use template)
src/types.tsAdd Exports to package.json
"exports": {
"./client": "./dist/client.js",
"./types": "./dist/types.js"
}
Create Seed Scripts (use template)
prisma/seed/index.tsprisma/seed/{entity}.ts (one per entity)Update Root Configuration (if needed)
pnpm-workspace.yamlturbo.jsonTest Build
pnpm --filter @metasaver/{project}-database build
packages/database/{project}-database/src, prisma/seeddist/@metasaver/{project}-database (scoped)0.1.0 (semantic versioning)"module" (ESM)tsconfig.json extends @metasaver/core-typescript-config/baserootDir: "./src"outDir: "./dist"prisma/schema.prisma exists"postgresql"{PROJECT_UPPER}_DATABASE_URL variableprisma-client-js@@map()@map()src/client.ts exists with singleton patternexport const prisma = ...src/types.ts exists (single file, not folder)export type * from "@prisma/client"package.json has exports field with /client and /types pathssrc/index.ts barrel export file.js extension (ESM)#/ aliasprisma/seed/index.ts existsprisma/seed/{entity}.ts.env.example (committed){PROJECT_UPPER}_DATABASE_URL in templatedotenv-cli wrapper for database scripts.env file is gitignored - never commit itpnpm build succeeds without errorspnpm lint:tscViolation: Using factory pattern instead of singleton
// INCORRECT - factory pattern
export function getPrismaClient(): PrismaClient {
if (!client) {
client = new PrismaClient();
}
return client;
}
Fix: Use simple singleton
// CORRECT - simple singleton
export const prisma = global.prisma || new PrismaClient();
if (process.env.NODE_ENV !== "production") {
global.prisma = prisma;
}
Violation: Creating types folder with pagination interfaces
// INCORRECT - src/types/index.ts with custom types
export interface PaginationOptions {
page?: number;
pageSize?: number;
}
Fix: Simple type re-export
// CORRECT - src/types.ts with Prisma re-export
export type * from "@prisma/client";
Violation: Including repository pattern in database package
// INCORRECT - src/repositories/base.repository.ts
export abstract class BaseRepository<T> {
// ...
}
Fix: Remove repository pattern from database package
// CORRECT - consumers use Prisma directly or implement repositories
// Database package only provides client and types
Violation: Schema without standard timestamps
// INCORRECT
model User {
id String @id @default(uuid())
email String
}
Fix: Add standard timestamp fields
// CORRECT
model User {
id String @id @default(uuid())
email String
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("users")
}
Violation: Seed scripts not idempotent
// INCORRECT - creates duplicates on re-run
async function seedUsers(prisma: PrismaClient) {
await prisma.user.create({ data: user });
}
Fix: Use upsert for idempotency
// CORRECT - safe to run multiple times
async function seedUsers(prisma: PrismaClient) {
await prisma.user.upsert({
where: { id: user.id },
update: { name: user.name },
create: user,
});
}
Audit Steps:
Check directory structure:
ls -la packages/database/rugby-crm-database/
ls -la packages/database/rugby-crm-database/src/
Validate package.json:
grep '"name"' packages/database/rugby-crm-database/package.json
grep '"build"' packages/database/rugby-crm-database/package.json
Check Prisma schema:
grep 'datasource db' packages/database/rugby-crm-database/prisma/schema.prisma
grep 'DATABASE_URL' packages/database/rugby-crm-database/prisma/schema.prisma
Verify client pattern:
grep 'export const prisma' packages/database/rugby-crm-database/src/client.ts
grep 'global.prisma' packages/database/rugby-crm-database/src/client.ts
Check types export:
grep 'export type' packages/database/rugby-crm-database/src/types.ts
Run build test:
pnpm --filter @metasaver/rugby-crm-database build
Files to Create (use templates):
packages/database/inventory-database/package.jsonpackages/database/inventory-database/tsconfig.jsonpackages/database/inventory-database/.env.examplepackages/database/inventory-database/prisma/schema.prismapackages/database/inventory-database/src/client.tspackages/database/inventory-database/src/types.tspackages/database/inventory-database/src/index.tspackages/database/inventory-database/prisma/seed/index.tspackages/database/inventory-database/prisma/seed/product.tsBuild and Test:
pnpm --filter @metasaver/inventory-database build
pnpm --filter @metasaver/inventory-database db:push
pnpm --filter @metasaver/inventory-database db:seed
Steps:
Add model to prisma/schema.prisma:
model Product {
id String @id @default(uuid())
name String
price Float
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
@@map("products")
}
Create migration:
pnpm --filter @metasaver/inventory-database db:migrate:dev
Add seed data in prisma/seed/product.ts:
export async function seedProducts(prisma: PrismaClient) {
const products = [{ id: "prod-1", name: "Product A", price: 10.0 }];
for (const product of products) {
await prisma.product.upsert({
where: { id: product.id },
update: { price: product.price },
create: product,
});
}
}
Update prisma/seed/index.ts:
await seedProducts(prisma);
Build and test:
pnpm --filter @metasaver/inventory-database build
pnpm --filter @metasaver/inventory-database db:seed