database/prisma-schema-starter/SKILL.md
Scaffold a Prisma 6.x project with schema.prisma (datasource, generator, models, relations, enums), migrations, seed script, Prisma Client usage, type-safe queries, and middleware.
npx skillsauth add achreftlili/deep-dev-skills prisma-schema-starterInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Scaffold a Prisma 6.x project with schema.prisma (datasource, generator, models, relations, enums), migrations, seed script, Prisma Client usage, type-safe queries, and middleware.
npm install @prisma/client
npm install -D prisma typescript @types/node tsx
npx prisma init --datasource-provider postgresql
# Creates:
# prisma/schema.prisma
# .env (with DATABASE_URL placeholder)
# Create the database and apply schema
npx prisma migrate dev --name init
# Seed initial data (if seed script exists)
npx prisma db seed
# Reset database (development only — drops and recreates)
npx prisma migrate reset
prisma/
schema.prisma # Data model, datasource, generators
migrations/ # Auto-generated migration SQL files
seed.ts # Database seed script
src/
lib/
prisma.ts # Singleton Prisma Client instance
modules/
users/
users.repository.ts # Data access using Prisma Client
schema.prisma file at prisma/schema.prisma (default path).@id with @default(uuid()) or @default(cuid()) for primary keys. Avoid auto-increment integers for distributed systems.npx prisma generate after every schema change to regenerate the client.npx prisma migrate dev during development; npx prisma migrate deploy in CI/production.seed script to package.json for prisma db seed.prisma/schema.prisma)generator client {
provider = "prisma-client-js"
}
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
}
enum Role {
USER
ADMIN
}
enum PostStatus {
DRAFT
PUBLISHED
ARCHIVED
}
model User {
id String @id @default(uuid())
email String @unique
name String
password String
role Role @default(USER)
posts Post[]
profile Profile?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@map("users")
}
model Profile {
id String @id @default(uuid())
bio String?
avatar String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
userId String @unique
@@map("profiles")
}
model Post {
id String @id @default(uuid())
title String
content String?
status PostStatus @default(DRAFT)
author User @relation(fields: [authorId], references: [id], onDelete: Cascade)
authorId String
tags Tag[]
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@index([authorId])
@@index([status])
@@map("posts")
}
model Tag {
id String @id @default(uuid())
name String @unique
posts Post[]
@@map("tags")
}
src/lib/prisma.ts)import { PrismaClient } from "@prisma/client";
const globalForPrisma = globalThis as unknown as {
prisma: PrismaClient | undefined;
};
export const prisma =
globalForPrisma.prisma ??
new PrismaClient({
log:
process.env.NODE_ENV === "development"
? ["query", "warn", "error"]
: ["error"],
});
if (process.env.NODE_ENV !== "production") {
globalForPrisma.prisma = prisma;
}
import { PrismaClient } from "@prisma/client";
// Soft-delete extension
const prisma = new PrismaClient().$extends({
query: {
$allModels: {
async findMany({ args, query }) {
args.where = { ...args.where, deletedAt: null };
return query(args);
},
async delete({ args, query }) {
return (query as any)({
...args,
data: { deletedAt: new Date() },
});
},
},
},
});
export { prisma };
src/modules/users/users.repository.ts)import { prisma } from "@/lib/prisma";
import type { Prisma } from "@prisma/client";
export async function createUser(data: Prisma.UserCreateInput) {
return prisma.user.create({
data,
select: {
id: true,
email: true,
name: true,
role: true,
createdAt: true,
},
});
}
export async function findUserByEmail(email: string) {
return prisma.user.findUnique({
where: { email },
include: { profile: true },
});
}
export async function findUserWithPosts(userId: string) {
return prisma.user.findUnique({
where: { id: userId },
include: {
posts: {
where: { status: "PUBLISHED" },
orderBy: { createdAt: "desc" },
take: 10,
},
},
});
}
export async function listUsers(params: {
page: number;
limit: number;
role?: "USER" | "ADMIN";
}) {
const { page, limit, role } = params;
const where: Prisma.UserWhereInput = role ? { role } : {};
const [users, total] = await prisma.$transaction([
prisma.user.findMany({
where,
skip: (page - 1) * limit,
take: limit,
orderBy: { createdAt: "desc" },
select: {
id: true,
email: true,
name: true,
role: true,
createdAt: true,
_count: { select: { posts: true } },
},
}),
prisma.user.count({ where }),
]);
return { users, total, pages: Math.ceil(total / limit) };
}
// Required models for @auth/prisma-adapter (NextAuth v5)
model Account {
id String @id @default(cuid())
userId String
type String
provider String
providerAccountId String
refresh_token String?
access_token String?
expires_at Int?
token_type String?
scope String?
id_token String?
session_state String?
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
@@unique([provider, providerAccountId])
}
model Session {
id String @id @default(cuid())
sessionToken String @unique
userId String
expires DateTime
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}
model VerificationToken {
identifier String
token String @unique
expires DateTime
@@unique([identifier, token])
}
Add these relations to the User model: accounts Account[] and sessions Session[].
prisma/seed.ts)import { PrismaClient } from "@prisma/client";
const prisma = new PrismaClient();
async function main() {
// Upsert to make seed idempotent
const admin = await prisma.user.upsert({
where: { email: "[email protected]" },
update: {},
create: {
email: "[email protected]",
name: "Admin User",
password: "$2b$10$hashedpassword", // Use bcrypt in real seeds
role: "ADMIN",
profile: {
create: {
bio: "System administrator",
},
},
},
});
const tags = await Promise.all(
["TypeScript", "Prisma", "Node.js"].map((name) =>
prisma.tag.upsert({
where: { name },
update: {},
create: { name },
})
)
);
await prisma.post.upsert({
where: { id: "seed-post-1" },
update: {},
create: {
id: "seed-post-1",
title: "Getting Started with Prisma",
content: "Prisma is a next-generation ORM for Node.js and TypeScript.",
status: "PUBLISHED",
authorId: admin.id,
tags: { connect: tags.map((t) => ({ id: t.id })) },
},
});
console.log("Seed completed");
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});
{
"prisma": {
"seed": "tsx prisma/seed.ts"
}
}
# Generate Prisma Client after schema changes
npx prisma generate
# Create and apply a migration (development)
npx prisma migrate dev --name <migration-name>
# Apply pending migrations (CI/production)
npx prisma migrate deploy
# Reset database (drop, recreate, apply migrations, seed)
npx prisma migrate reset
# Seed database
npx prisma db seed
# Open Prisma Studio (visual database editor)
npx prisma studio
# Format schema file
npx prisma format
# Push schema without migrations (prototyping only)
npx prisma db push
# Pull schema from existing database
npx prisma db pull
# Check migration status
npx prisma migrate status
@prisma/client directly in services or wrap in a NestJS module. Pair with nestjs-project-starter skill.@auth/prisma-adapter for session/account storage. Pair with nextauth-skill.jest-mock-extended or use a test database with prisma migrate reset before each test suite.db service in docker-compose. Run npx prisma migrate deploy in the app entrypoint or init container.testing
Set up Vitest 2.x with TypeScript for unit and component testing using test/describe/it, vi.fn/vi.mock/vi.spyOn, component testing with Testing Library, coverage (v8/istanbul), workspace config, and snapshot testing.
testing
Set up pytest 8.x with Python for unit and integration testing using fixtures (scope, autouse, parametrize), async tests (pytest-asyncio), mocking (unittest.mock, pytest-mock), coverage (pytest-cov), conftest.py patterns, and markers.
testing
Set up Playwright 1.49+ with TypeScript for E2E testing using page object model, fixtures, test.describe/test blocks, assertions, selectors, network mocking, CI configuration, and trace viewer.
testing
Set up Jest 30+ with TypeScript for unit tests, integration tests, mocking (jest.fn, jest.mock, jest.spyOn), coverage configuration, custom matchers, snapshot testing, and setup/teardown patterns.