Expert knowledge for migrating projects to OpenSaaS Stack. Use when discussing migration strategies, access control patterns, or OpenSaaS Stack configuration best practices.
Provides expert guidance for migrating projects from Prisma, KeystoneJS, or Next.js to OpenSaaS Stack. Use when planning migrations, configuring access control patterns, or troubleshooting migration issues.
/plugin marketplace add OpenSaasAU/stack/plugin install opensaas-migration@opensaas-stack-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Expert guidance for migrating existing projects to OpenSaaS Stack.
Use this skill when:
opensaas.config.tsIMPORTANT: Always install packages before starting migration
Detect the user's package manager (check for package-lock.json, pnpm-lock.yaml, yarn.lock, or bun.lockb) and use their preferred package manager.
Required packages:
# Using npm
npm install --save-dev @opensaas/stack-cli
npm install @opensaas/stack-core
# Using pnpm
pnpm add -D @opensaas/stack-cli
pnpm add @opensaas/stack-core
# Using yarn
yarn add -D @opensaas/stack-cli
yarn add @opensaas/stack-core
# Using bun
bun add -D @opensaas/stack-cli
bun add @opensaas/stack-core
Optional packages (based on user needs):
@opensaas/stack-auth - If the project needs authentication@opensaas/stack-ui - If the project needs the admin UI@opensaas/stack-tiptap - If the project needs rich text editing@opensaas/stack-storage - If the project needs file storage@opensaas/stack-rag - If the project needs semantic search/RAGDatabase adapters (required for Prisma 7):
SQLite:
npm install better-sqlite3 @prisma/adapter-better-sqlite3
PostgreSQL:
npm install pg @prisma/adapter-pg
Neon (serverless PostgreSQL):
npm install @neondatabase/serverless @prisma/adapter-neon ws
IMPORTANT: For KeystoneJS projects, uninstall KeystoneJS packages before installing OpenSaaS
KeystoneJS migrations should preserve the existing file structure and just swap packages. Do NOT create a new project structure.
# Detect package manager and uninstall KeystoneJS packages
npm uninstall @keystone-6/core @keystone-6/auth @keystone-6/fields-document
# Or with pnpm
pnpm remove @keystone-6/core @keystone-6/auth @keystone-6/fields-document
Remove all @keystone-6/* packages from package.json.
Prisma Projects:
schema.prismaKeystoneJS Projects:
keystone.config.ts or keystone.tsCommon Patterns:
// Public read, authenticated write
operation: {
query: () => true,
create: ({ session }) => !!session?.userId,
update: ({ session }) => !!session?.userId,
delete: ({ session }) => !!session?.userId,
}
// Author-only access
operation: {
query: () => true,
update: ({ session, item }) => item.authorId === session?.userId,
delete: ({ session, item }) => item.authorId === session?.userId,
}
// Admin-only
operation: {
query: ({ session }) => session?.role === 'admin',
create: ({ session }) => session?.role === 'admin',
update: ({ session }) => session?.role === 'admin',
delete: ({ session }) => session?.role === 'admin',
}
// Filter-based access
operation: {
query: ({ session }) => ({
where: { authorId: { equals: session?.userId } }
}),
}
Prisma to OpenSaaS:
| Prisma Type | OpenSaaS Field |
|---|---|
String | text() |
Int | integer() |
Boolean | checkbox() |
DateTime | timestamp() |
Enum | select({ options: [...] }) |
Relation | relationship({ ref: '...' }) |
KeystoneJS to OpenSaaS:
| KeystoneJS Field | OpenSaaS Field |
|---|---|
text | text() |
integer | integer() |
checkbox | checkbox() |
timestamp | timestamp() |
select | select() |
relationship | relationship() |
password | password() |
SQLite (Development):
import { PrismaBetterSQLite3 } from '@prisma/adapter-better-sqlite3'
import Database from 'better-sqlite3'
export default config({
db: {
provider: 'sqlite',
url: process.env.DATABASE_URL || 'file:./dev.db',
prismaClientConstructor: (PrismaClient) => {
const db = new Database(process.env.DATABASE_URL || './dev.db')
const adapter = new PrismaBetterSQLite3(db)
return new PrismaClient({ adapter })
},
},
})
PostgreSQL (Production):
import { PrismaPg } from '@prisma/adapter-pg'
import pg from 'pg'
export default config({
db: {
provider: 'postgresql',
url: process.env.DATABASE_URL,
prismaClientConstructor: (PrismaClient) => {
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL })
const adapter = new PrismaPg(pool)
return new PrismaClient({ adapter })
},
},
})
CRITICAL: KeystoneJS projects should be migrated IN PLACE
Do NOT create a new project structure. Instead:
Keep existing files and update them:
Rename config file:
keystone.config.ts → opensaas.config.tskeystone.ts → opensaas.config.tsUpdate imports in ALL files:
// Before (KeystoneJS)
import { config, list } from '@keystone-6/core'
import { text, relationship, timestamp } from '@keystone-6/core/fields'
// After (OpenSaaS)
import { config, list } from '@opensaas/stack-core'
import { text, relationship, timestamp } from '@opensaas/stack-core/fields'
Rename KeystoneJS concepts to OpenSaaS:
keystone.config.ts → opensaas.config.tsKeystone references → OpenSaaS or remove entirelyUpdate schema/list definitions:
@keystone-6/core/fields to @opensaas/stack-core/fieldsPreserve API routes and pages:
| KeystoneJS Import | OpenSaaS Import |
|---|---|
@keystone-6/core | @opensaas/stack-core |
@keystone-6/core/fields | @opensaas/stack-core/fields |
@keystone-6/auth | @opensaas/stack-auth |
@keystone-6/fields-document | @opensaas/stack-tiptap |
Before (keystone.config.ts):
import { config, list } from '@keystone-6/core'
import { text, relationship, timestamp } from '@keystone-6/core/fields'
export default config({
db: {
provider: 'postgresql',
url: process.env.DATABASE_URL,
},
lists: {
Post: list({
fields: {
title: text({ validation: { isRequired: true } }),
content: text({ ui: { displayMode: 'textarea' } }),
author: relationship({ ref: 'User.posts' }),
publishedAt: timestamp(),
},
}),
},
})
After (opensaas.config.ts):
import { config, list } from '@opensaas/stack-core'
import { text, relationship, timestamp } from '@opensaas/stack-core/fields'
import { PrismaPg } from '@prisma/adapter-pg'
import pg from 'pg'
export default config({
db: {
provider: 'postgresql',
url: process.env.DATABASE_URL,
prismaClientConstructor: (PrismaClient) => {
const pool = new pg.Pool({ connectionString: process.env.DATABASE_URL })
const adapter = new PrismaPg(pool)
return new PrismaClient({ adapter })
},
},
lists: {
Post: list({
fields: {
title: text({ validation: { isRequired: true } }),
content: text(), // Note: OpenSaaS text() doesn't have ui.displayMode
author: relationship({ ref: 'User.posts' }),
publishedAt: timestamp(),
},
}),
},
})
keystone.config.ts to opensaas.config.ts@keystone-6/core → @opensaas/stack-core@keystone-6/core/fields → @opensaas/stack-core/fields@keystone-6/auth → @opensaas/stack-authDO NOT:
DO:
Solution:
opensaas generate to create Prisma schemaprisma db push instead of migrations for existing databasesprisma migrate dev with existing dataSolution:
Solution:
BaseFieldConfiggetZodSchema, getPrismaType, getTypeScriptTypeSolution:
@opensaas/stack-tiptap rich text fieldopensaas.config.tsopensaas generate (or npx opensaas generate)prisma generate (or npx prisma generate)prisma db push (or npx prisma db push)keystone.config.ts to opensaas.config.tsopensaas generateprisma generateprisma db pushcontext.db@opensaas/stack-auth for authenticationopensaas.config.ts to gitWhen you encounter bugs or missing features in OpenSaaS Stack:
If during migration you discover:
Use the github-issue-creator agent to create a GitHub issue on the OpenSaasAU/stack repository:
Invoke the github-issue-creator agent with:
- Clear description of the bug or missing feature
- Steps to reproduce (if applicable)
- Expected vs actual behavior
- Affected files and line numbers
- Your suggested solution (if you have one)
This ensures bugs and feature requests are properly tracked and addressed by the OpenSaaS Stack team, improving the experience for future users.
Example:
If you notice that the migration command doesn't properly handle Prisma enums, invoke the github-issue-creator agent:
"Found a bug: The migration generator doesn't convert Prisma enums to OpenSaaS select fields. Enums are being ignored during schema analysis in packages/cli/src/migration/introspectors/prisma-introspector.ts"
The agent will create a detailed GitHub issue with reproduction steps and proposed solution.