opensaas-migration
Expert knowledge for migrating projects to OpenSaaS Stack. Use when discussing migration strategies, access control patterns, or OpenSaaS Stack configuration best practices.
$ 安裝
git clone https://github.com/OpenSaasAU/stack /tmp/stack && cp -r /tmp/stack/claude-plugins/opensaas-migration/skills/opensaas-migration ~/.claude/skills/stack// tip: Run this command in your terminal to install the skill
name: opensaas-migration description: Expert knowledge for migrating projects to OpenSaaS Stack. Use when discussing migration strategies, access control patterns, or OpenSaaS Stack configuration best practices.
OpenSaaS Stack Migration
Expert guidance for migrating existing projects to OpenSaaS Stack.
When to Use This Skill
Use this skill when:
- Planning a migration from Prisma, KeystoneJS, or Next.js
- Designing access control patterns
- Configuring
opensaas.config.ts - Troubleshooting migration issues
- Explaining OpenSaaS Stack concepts
Migration Process
1. Install Required Packages
IMPORTANT: 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/RAG
Database 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
2. Uninstall Old Packages (KeystoneJS Only)
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.
3. Schema Analysis
Prisma Projects:
- Analyze existing
schema.prisma - Identify models, fields, and relationships
- Note any Prisma-specific features used
KeystoneJS Projects:
- Review list definitions in
keystone.config.tsorkeystone.ts - Map KeystoneJS fields to OpenSaaS fields
- Identify access control patterns
- Note the existing file structure - preserve it during migration
4. Access Control Design
Common 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 } }
}),
}
5. Field Mapping
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() |
6. Database Configuration
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 })
},
},
})
KeystoneJS Migration Strategy
CRITICAL: KeystoneJS projects should be migrated IN PLACE
Do NOT create a new project structure. Instead:
File Structure Preservation
Keep existing files and update them:
-
Rename config file:
keystone.config.ts→opensaas.config.ts- OR
keystone.ts→opensaas.config.ts
-
Update 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.tsKeystonereferences →OpenSaaSor remove entirely- Keep all other file names and structure as-is
-
Update schema/list definitions:
- Keep existing list definitions
- Update field imports from
@keystone-6/core/fieldsto@opensaas/stack-core/fields - Adapt access control syntax (KeystoneJS and OpenSaaS are similar)
- Keep existing GraphQL API file structure
-
Preserve API routes and pages:
- Keep existing Next.js pages
- Update any KeystoneJS context calls to use OpenSaaS context
- Maintain existing route structure
Import Mapping
| 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 |
Example: KeystoneJS to OpenSaaS Config
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(),
},
}),
},
})
Steps for KeystoneJS Migration
- Uninstall KeystoneJS packages (see step 2 above)
- Install OpenSaaS packages (see step 1 above)
- Rename
keystone.config.tstoopensaas.config.ts - Find and replace in ALL project files:
@keystone-6/core→@opensaas/stack-core@keystone-6/core/fields→@opensaas/stack-core/fields@keystone-6/auth→@opensaas/stack-auth
- Add Prisma adapter to database config (required for Prisma 7)
- Update context creation in API routes/pages
- Test - the app structure should remain identical
DO NOT:
- Create new folders or reorganize the project
- Move files to different locations
- Create a new "OpenSaaS structure"
- Change API endpoints or routes
DO:
- Keep existing file structure
- Update imports only
- Adapt config to OpenSaaS syntax
- Preserve existing API routes and pages
Common Migration Challenges
Challenge: Preserving Existing Data
Solution:
- Use
opensaas generateto create Prisma schema - Use
prisma db pushinstead of migrations for existing databases - Never use
prisma migrate devwith existing data
Challenge: Complex Access Control
Solution:
- Start with simple boolean access control
- Iterate to filter-based access as needed
- Use field-level access for sensitive data
Challenge: Custom Field Types
Solution:
- Create custom field builders extending
BaseFieldConfig - Implement
getZodSchema,getPrismaType,getTypeScriptType - Register UI components for admin interface
Challenge: KeystoneJS Document Field
Solution:
- Replace with
@opensaas/stack-tiptaprich text field - Or create custom field type for document structure
- May require data migration for existing documents
Migration Checklist
For Prisma Projects:
- Detect package manager (npm, pnpm, yarn, or bun)
- Install required packages (@opensaas/stack-cli, @opensaas/stack-core)
- Install optional packages (auth, ui, etc. based on needs)
- Install database adapter (better-sqlite3, pg, etc.)
- Analyze existing schema
- Design access control patterns
- Create
opensaas.config.ts - Configure database adapter in config
- Run
opensaas generate(ornpx opensaas generate) - Run
prisma generate(ornpx prisma generate) - Run
prisma db push(ornpx prisma db push) - Test access control
- Verify admin UI (if using @opensaas/stack-ui)
- Update application code to use context
- Test all CRUD operations
- Deploy to production
For KeystoneJS Projects:
- Detect package manager (npm, pnpm, yarn, or bun)
- Uninstall ALL KeystoneJS packages (@keystone-6/*)
- Install required packages (@opensaas/stack-cli, @opensaas/stack-core)
- Install optional packages (auth, ui, tiptap for document fields)
- Install database adapter (pg, @prisma/adapter-pg for PostgreSQL)
- Rename
keystone.config.tstoopensaas.config.ts - Update imports in config file (KeystoneJS → OpenSaaS)
- Find and replace imports in ALL project files
- Add Prisma adapter to database config
- Update context creation in API routes
- Analyze and adapt access control patterns
- Run
opensaas generate - Run
prisma generate - Run
prisma db push - Test existing API routes
- Test existing pages/UI
- Test all CRUD operations
- Verify no broken imports
- Deploy to production
Best Practices
- Start Simple: Begin with basic access control, refine later
- Test Access Control: Verify permissions work as expected
- Use Context Everywhere: Replace direct Prisma calls with
context.db - Leverage Plugins: Use
@opensaas/stack-authfor authentication - Version Control: Commit
opensaas.config.tsto git - Document Decisions: Comment complex access control logic
Reporting Issues
When you encounter bugs or missing features in OpenSaaS Stack:
If during migration you discover:
- Bugs in OpenSaaS Stack packages
- Missing features that would improve the migration experience
- Documentation gaps or errors
- API inconsistencies or unexpected behavior
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.
Resources
Repository
