Migration Guide: Free to Pro
Complete step-by-step guide to upgrade from the free version of Prisma Zod Generator to PZG Pro.
๐ Overviewโ
This guide will help you:
- Understand the differences between free and Pro versions
- Plan your migration strategy
- Execute the upgrade with minimal disruption
- Take advantage of new Pro features
Estimated Time: 30-60 minutes depending on your codebase size
๐ Pre-Migration Assessmentโ
1. Current Setup Analysisโ
First, understand your current setup:
# Check your current version
npm list prisma-zod-generator
# Review your Prisma schema
cat prisma/schema.prisma | grep -E "^model|^enum"
# Check generated files
ls -la prisma/generated/ # or wherever your schemas are generated
2. Compatibility Checkโ
Ensure your environment meets Pro requirements (Prisma 7 baseline):
- Node.js: 20.19+ (22.x recommended)
- Prisma: 7.0+
- TypeScript: 5.4+ (5.9 recommended)
- Zod: 4.x recommended (>=3.25 supported)
3. Feature Planningโ
Decide which Pro features you want to use:
- โ Server Actions: Generate Next.js server actions
- โ Policies & Redaction: Field-level security and PII protection
- โ SDK Publisher: Generate client SDKs
- โ Drift Guard: Schema change detection in CI
๐ Getting Your Licenseโ
1. Choose Your Planโ
Visit the pricing page and select:
- Starter ($69/year) โ 1 developer, Server Actions + Forms (CLI plan slug
starter) - Professional ($199/year) โ up to 5 developers, security & governance suite (
professional) - Business ($599/year) โ unlimited developers, platform toolkits (
business) - Enterprise (Custom) โ unlimited developers, multi-tenant kit, roadmap partnership (
enterprise)
2. Purchase & Downloadโ
After purchase, you'll receive:
- License key via email
- Access to Pro features
- Priority response targets (Business/Enterprise) and roadmap reviews (Enterprise)
3. Verify Licenseโ
# Set your license key
export PZG_LICENSE_KEY=pzg_v2_your_license_key_here
export PZG_LICENSE_PUBLIC_KEY='-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAwRNEnFQJgBdNnwvnTTAPySp223shjXfioII2qMkqBFQ=\n-----END PUBLIC KEY-----' # replace with the public key provided to you
# Verify it works
npx prisma-zod-generator license-check
๐ง Installation & Setupโ
1. Update Packageโ
The Pro features are included in the same package:
# Update to latest version
npm update prisma-zod-generator
# Or with pnpm
pnpm update prisma-zod-generator
# Or with yarn
yarn upgrade prisma-zod-generator
2. Environment Configurationโ
Add your license key to environment variables:
# Option 1: Shell profile (.bashrc, .zshrc)
cat <<'EOF' >> ~/.bashrc
export PZG_LICENSE_KEY=pzg_v2_your_license_key_here
export PZG_LICENSE_PUBLIC_KEY='-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAwRNEnFQJgBdNnwvnTTAPySp223shjXfioII2qMkqBFQ=
-----END PUBLIC KEY-----'
EOF
# Option 2: Project .env file
cat <<'EOF' >> .env
PZG_LICENSE_KEY=pzg_v2_your_license_key_here
PZG_LICENSE_PUBLIC_KEY='-----BEGIN PUBLIC KEY-----
MCowBQYDK2VwAyEAwRNEnFQJgBdNnwvnTTAPySp223shjXfioII2qMkqBFQ=
-----END PUBLIC KEY-----'
EOF
๐๏ธ Migration Strategyโ
Strategy 1: Gradual Migration (Recommended)โ
Migrate one feature at a time to minimize risk:
- Week 1: Set up license and basic configuration
- Week 2: Add Server Actions to one model
- Week 3: Add Policies to sensitive fields
- Week 4: Set up Drift Guard in CI
- Week 5: Generate SDK for external consumers
Strategy 2: Full Migrationโ
Migrate all features at once (for smaller projects):
- Set up license and configuration
- Generate all Pro features
- Update application code
- Test thoroughly
- Deploy
Strategy 3: Feature-by-Featureโ
Migrate based on business priorities:
- Security First: Start with Policies & Redaction
- Developer Experience: Add Server Actions
- API Stability: Implement Drift Guard
- External Integrations: Generate SDK
๐ Adding Policies & Redactionโ
1. Identify Sensitive Fieldsโ
Review your Prisma schema for:
- PII Data: email, phone, ssn, address
- Sensitive Data: salary, medical info, financial data
- Access-Controlled Data: admin-only fields
2. Add Policy Commentsโ
Update your Prisma schema with policy annotations:
Read policies go on the model โ they narrow the where clause of every generated read. @pii
goes on the field:
/// @policy read:where tenantId == ctx.tenantId
/// @policy deny:role in ["guest"]
model User {
id String @id @default(cuid())
tenantId String
/// @pii email redact:logs mask:partial
email String @unique
/// @policy deny:role in ["admin"]
salary Int? // present in UserAdminSchema, omitted from the other role schemas
role Role @default(USER)
createdAt DateTime @default(now())
}
/// @policy read:where userId == ctx.userId
model Post {
id String @id @default(cuid())
title String
content String
published Boolean @default(false)
userId String
user User @relation(fields: [userId], references: [id])
}
Three condition forms are implemented: role in ["a", "b"], userId == ctx.userId and
tenantId == ctx.tenantId. Anything else โ an arbitrary field comparison like published eq true,
or a boolean combination with OR โ is reported at generation time and refused at runtime rather
than silently leaving the query unscoped. Use one annotation per condition. write: is not
implemented; use deny:, create:, update: or delete:.
3. Generate Policiesโ
Ensure enablePolicies = true in your generator pzgPro block, then run:
pnpm exec prisma generate
ls prisma/generated/pro/policies/
4. Integrate in Applicationโ
// Example: API route with policy enforcement
import { createSafeUserOperations, UserRedactor } from '@/generated/pro/policies';
export async function GET(request: Request) {
// Read policies are folded into the where clause โ query through the wrapper
// rather than filtering rows after the fact.
const users = createSafeUserOperations(prisma, { tenantId: getTenantId() });
const visible = await users.findMany({
userId: getCurrentUserId(),
role: getUserRole(),
});
// Redact @pii fields for logs
const redactor = new UserRedactor();
console.log('Users fetched:', redactor.redact(visible, 'logs'));
return Response.json(visible);
}
PolicyContext carries userId, role and tenantId (plus anything else you
add). Note it is role, singular โ there is no roles array.
โก Adding Server Actionsโ
1. Generate Server Actionsโ
Set enableServerActions = true in schema.prisma, then:
pnpm exec prisma generate
ls prisma/generated/pro/server-actions/
Need only a subset of models? Point
configPathat your generator config and mark the models you want left out โ the samemodelsblock the core generator uses, so one file governs both:{ "models": { "AuditLog": { "enabled": false }, "SessionToken": { "enabled": false } } }Every pack respects this, so an excluded model gets no endpoints, no form, no SDK method and no policy wrapper. Requires 2.4.0+; earlier versions generated for every model in the schema.
2. Review Generated Codeโ
# Check generated server actions
ls prisma/generated/pro/server-actions/actions/
ls prisma/generated/pro/server-actions/hooks/
Add the framework directives before wiring anything up: 'use server' at the top
of each actions/<model>.ts, and 'use client' at the top of each
hooks/use<Model>.ts. The generator does not emit them, and a production
next build needs both.
3. Update Your Componentsโ
Replace manual API calls with generated hooks:
Before (Free Version):
// Manual API call
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const createUser = async (data) => {
setLoading(true);
try {
const response = await fetch('/api/users', {
method: 'POST',
body: JSON.stringify(data)
});
const user = await response.json();
// Handle success
} catch (err) {
setError(err);
} finally {
setLoading(false);
}
};
After (Pro Version):
// Generated hook with validation
import { useCreateUser } from '@/server/hooks/useUser';
const { create, isPending, error } = useCreateUser();
const handleSubmit = async (data) => {
try {
await create(data); // Automatic validation + policies
// Success handled by hook
} catch (err) {
// Error handled by hook
}
};
4. Add Required Dependenciesโ
# Install React Query (for hooks)
npm install @tanstack/react-query
# Add to your app root
// app/layout.tsx or pages/_app.tsx
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
const queryClient = new QueryClient();
export default function RootLayout({ children }) {
return (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);
}
๐ฆ Generating SDKโ
1. Configure SDK Settingsโ
Add the flag and JSON config to generator pzgPro:
generator pzgPro {
provider = "node ./node_modules/prisma-zod-generator/lib/cli/pzg-pro.js"
output = "./generated/pro"
enableSDK = true
sdk = "{ \"platforms\": [\"typescript\", \"python\"] }"
}
platforms selects which clients to emit; outputPath overrides where they go.
The remaining SDKConfig keys (packageName, version, publishRegistry,
enableAutoPublish, includeDocumentation, authConfig) are accepted but do not
currently change the output.
2. Generate SDKโ
pnpm exec prisma generate
ls prisma/generated/pro/sdk/typescript/index.ts
ls prisma/generated/pro/sdk/python/api_client.py
3. Consume the clientโ
The pack emits standalone source files, not a publishable package โ there is no
package.json and no build step. Either import the file directly from your app,
or copy it into a package you maintain:
cp prisma/generated/pro/sdk/typescript/index.ts packages/api-sdk/src/client.ts
4. Use SDK in Client Applicationsโ
import { APIClient } from './generated/pro/sdk/typescript';
// Positional arguments: base URL, then an optional key sent as
// `Authorization: Bearer <key>`.
const client = new APIClient('https://api.yourapp.com', process.env.API_TOKEN);
// Methods are flat and named `<verb><Model>` โ there are no per-model resource
// objects (`client.users.findMany()` does not exist).
const orgs = await client.listOrganizations();
const org = await client.getOrganization(id);
const member = await client.createMember({
email: 'user@example.com',
name: 'John Doe',
});
๐จ Setting Up Drift Guardโ
1. Create GitHub Workflowโ
# .github/workflows/pzg-drift-guard.yml
name: PZG Drift Guard
on:
pull_request:
branches: [main]
jobs:
schema-drift:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run Drift Guard
env:
PZG_LICENSE_KEY: ${{ secrets.PZG_LICENSE_KEY }}
PZG_LICENSE_PUBLIC_KEY: ${{ secrets.PZG_LICENSE_PUBLIC_KEY }}
run: npx pzg-pro guard --schema=./prisma/schema.prisma --base origin/main --head HEAD --format github
2. Add GitHub Secretโ
- Go to your repository Settings > Secrets and variables > Actions
- Add
PZG_LICENSE_KEYwith your license key value andPZG_LICENSE_PUBLIC_KEYwith the PEM public key provided to you - Make sure it's marked as a secret (masked in logs)
3. Test Drift Guardโ
# Test locally against main
npx pzg-pro guard --schema=./prisma/schema.prisma --base main --head HEAD --format github
# Produce JSON output for scripts
npx pzg-pro guard --format json
๐งช Testing Your Migrationโ
1. Validation Testsโ
Create tests to ensure your migration works:
// tests/migration.test.ts
import { validateLicense } from 'prisma-zod-generator/lib/license';
import { createSafeUserOperations, UserRedactor } from '@/generated/pro/policies';
describe('PZG Pro Migration', () => {
test('license is valid', async () => {
const license = await validateLicense(false);
expect(license).toBeTruthy();
expect(license.plan).toBeDefined();
});
test('policies are generated', () => {
expect(createSafeUserOperations).toBeDefined();
expect(new UserRedactor().redact).toBeDefined();
});
test('server actions exist', async () => {
const { createUser } = await import('@/generated/pro/server-actions/actions/user');
expect(createUser).toBeDefined();
});
});
2. Integration Testsโ
Test the full flow with real data:
// tests/integration.test.ts
import { createSafeUserOperations } from '@/generated/pro/policies';
import { validateUserCreate } from '@/generated/pro/policies/dto/user';
describe('Pro Features Integration', () => {
test('read policies scope the query', async () => {
const users = createSafeUserOperations(prisma, { tenantId: 'tenant-a' });
// The wrapper folds the model's @policy read rule into the where clause,
// so rows outside the caller's scope are never fetched.
const visible = await users.findMany({ userId: 'regular-user', role: 'user' });
expect(visible.every((u) => u.tenantId === 'tenant-a')).toBe(true);
});
test('DTO validation rejects bad input', () => {
expect(() => validateUserCreate({ email: 'not-an-email' })).toThrow();
});
});
3. End-to-End Testsโ
Test the complete user journey:
// e2e/pro-features.spec.ts
import { test, expect } from '@playwright/test';
test('user creation with validation', async ({ page }) => {
await page.goto('/users/new');
// Fill invalid data
await page.fill('[name="email"]', 'invalid-email');
await page.click('button[type="submit"]');
// Should show validation error from Zod schema
await expect(page.locator('.error')).toContainText('Invalid email');
// Fill valid data
await page.fill('[name="email"]', 'user@example.com');
await page.fill('[name="name"]', 'John Doe');
await page.click('button[type="submit"]');
// Should redirect to users list
await expect(page).toHaveURL('/users');
});
๐ Monitoring Your Migrationโ
1. Performance Metricsโ
Monitor the impact of Pro features:
// utils/monitoring.ts
export function trackPolicyPerformance() {
const start = performance.now();
return {
end: () => {
const duration = performance.now() - start;
console.log(`Policy validation took ${duration}ms`);
// Send to analytics
if (typeof window !== 'undefined') {
// Client-side tracking
}
}
};
}
2. Error Trackingโ
Set up error monitoring for Pro features:
// utils/error-tracking.ts
export function trackProError(error: Error, feature: string) {
console.error(`PZG Pro ${feature} error:`, error);
// Send to error tracking service
if (process.env.NODE_ENV === 'production') {
// Sentry, Bugsnag, etc.
}
}
3. Usage Analyticsโ
Track Pro feature adoption:
// utils/analytics.ts
export function trackProFeatureUsage(feature: string, action: string) {
// Track feature usage
console.log(`PZG Pro feature used: ${feature}.${action}`);
// Send to analytics service
}
๐ Deployment Considerationsโ
1. Environment Variablesโ
Ensure license keys are properly set in all environments:
# Development
export PZG_LICENSE_KEY=pzg_v2_dev_key
export PZG_LICENSE_PUBLIC_KEY='-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAwRNEnFQJgBdNnwvnTTAPySp223shjXfioII2qMkqBFQ=\n-----END PUBLIC KEY-----'
# Staging
export PZG_LICENSE_KEY=pzg_v2_staging_key
export PZG_LICENSE_PUBLIC_KEY='-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAwRNEnFQJgBdNnwvnTTAPySp223shjXfioII2qMkqBFQ=\n-----END PUBLIC KEY-----'
# Production
export PZG_LICENSE_KEY=pzg_v2_prod_key
export PZG_LICENSE_PUBLIC_KEY='-----BEGIN PUBLIC KEY-----\nMCowBQYDK2VwAyEAwRNEnFQJgBdNnwvnTTAPySp223shjXfioII2qMkqBFQ=\n-----END PUBLIC KEY-----'
2. Build Processโ
Update your build scripts to generate Pro features:
{
"scripts": {
"build": "pnpm exec prisma generate && next build",
"generate:pro": "pnpm exec prisma generate",
"prebuild": "pnpm exec prisma generate"
}
}
3. CI/CD Pipelineโ
Update your deployment pipeline:
# Deploy workflow
- name: Generate Pro Features
env:
PZG_LICENSE_KEY: ${{ secrets.PZG_LICENSE_KEY }}
PZG_LICENSE_PUBLIC_KEY: ${{ secrets.PZG_LICENSE_PUBLIC_KEY }}
run: |
npm run generate:pro
- name: Build Application
run: npm run build
- name: Run Tests
run: npm test
โ Post-Migration Checklistโ
Immediate (Day 1)โ
- License validation works in all environments
- Generated code compiles without errors
- Basic functionality works (create, read, update, delete)
- CI/CD pipeline passes
- No breaking changes in existing functionality
Short-term (Week 1)โ
- All team members can use Pro features
- Policies are enforcing correctly
- Server Actions work in production
- Drift Guard detects schema changes
- Error monitoring is set up
Medium-term (Month 1)โ
- Performance is acceptable
- SDK is being used by external clients
- Policy violations are logged and monitored
- Business customers are comfortable with new features
- Documentation is updated
Long-term (Quarter 1)โ
- ROI is positive (time saved, security improved)
- Advanced features are being utilized
- Integration with other tools is working
- Business plan adopters report productivity gains
๐ Common Migration Issuesโ
Issue: "Invalid license key"โ
Solution:
- Check license key format and validity
- Ensure environment variable is set correctly
- Verify network connectivity for validation
Issue: Generated files have import errorsโ
Solution:
- Check output paths in configuration
- Ensure TypeScript can resolve imports
- Update tsconfig.json include paths
Issue: Policies not enforcingโ
Solution:
- Verify policy syntax in schema comments
- Check that policies are being called in code
- Ensure policy context is provided correctly
Issue: Server Actions not workingโ
Solution:
- Install required dependencies (@tanstack/react-query)
- Set up QueryClient provider
- Check Next.js version compatibility
Issue: CI/CD failuresโ
Solution:
- Add license key to CI environment secrets
- Ensure full git history is available (fetch-depth: 0)
- Install all dependencies in CI
๐ Getting Helpโ
Documentation Resourcesโ
Community Supportโ
Premium Supportโ
- Direct Support: DM @omardulaimidev on X (Professional, Business, Enterprise)
- Direct Support: Available for Business and Enterprise plans
- Migration Assistance: Custom migration help available
๐ Welcome to PZG Pro!โ
Congratulations on successfully migrating to PZG Pro! You now have access to:
- Enhanced Security: Field-level policies and PII redaction
- Better DX: Generated Server Actions and React hooks
- API Stability: Schema drift detection and prevention
- External Integration: Auto-generated client SDKs
- Priority Support: Direct help when you need it
Next Steps:
- Explore advanced Pro features
- Join our Discord community
- Share your success story
- Consider upgrading as your team grows
Happy coding with PZG Pro! ๐