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.
Limited to specific tools
Additional assets for this skill
This skill is limited to using the following tools:
INDEX.mdTEMPLATES.mdtemplates/client.ts.templatetemplates/index.ts.templatetemplates/package.json.templatetemplates/schema.prisma.templatetemplates/seed-entity.ts.templatetemplates/seed-index.ts.templatetemplates/tsconfig.json.templatetemplates/types.ts.templateThis 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 { prisma } from "../../src/client.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();
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 avoid 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"
}
}
src/index.ts barrel export:
export { prisma, default } from "./client.js";
export * from "./types.js";
Key Points:
.js extension (ESM compatibility)Create 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.tsCreate Main Export (use template)
src/index.tsCreate 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/ (git-ignored)@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"src/index.ts barrel export exists.js extension (ESM)prisma/seed/index.ts existsprisma/seed/{entity}.ts.env.example exists (committed){PROJECT_UPPER}_DATABASE_URLdotenv-cli wrapper.env file committed (gitignored)pnpm build succeeds without errorspnpm lint:tscViolation: Using factory pattern instead of singleton
// WRONG - factory pattern
export function getPrismaClient(): PrismaClient {
if (!client) {
client = new PrismaClient();
}
return client;
}
Fix: Use simple singleton
// RIGHT - 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
// WRONG - src/types/index.ts with custom types
export interface PaginationOptions {
page?: number;
pageSize?: number;
}
Fix: Simple type re-export
// RIGHT - src/types.ts with Prisma re-export
export type * from "@prisma/client";
Violation: Including repository pattern in database package
// WRONG - src/repositories/base.repository.ts
export abstract class BaseRepository<T> {
// ...
}
Fix: Remove repository pattern from database package
// RIGHT - consumers use Prisma directly or implement repositories
// Database package only provides client and types
Violation: Schema without standard timestamps
// WRONG
model User {
id String @id @default(uuid())
email String
}
Fix: Add standard timestamp fields
// RIGHT
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
// WRONG - creates duplicates on re-run
async function seedUsers(prisma: PrismaClient) {
await prisma.user.create({ data: user });
}
Fix: Use upsert for idempotency
// RIGHT - 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