Zenstack migrate from v2
Agent skills for ZenStack, published to skills.sh — ZModel schema modeling, access control, querying, automatic CRUD service, migrations, and plugin development.
npx -y skills add zenstackhq/skills --skill zenstack-migrate-from-v2Assembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 3 stars3 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
Upgrade an existing ZenStack V2 project to V3. Use when renaming V2 packages (zenstack, @zenstackhq/runtime), moving access control to the policy plugin, converting future() to post-update/before(), replacing abstract models with types+mixins, updating server adapters (getPrisma → getClient), and migrating client-side TanStack Query hooks.
SKILL.md
5.2 KB, as published. Nobody here has run it
Migrating from ZenStack V2 to V3
ZenStack V3 is a major rewrite: the Prisma ORM engine is replaced with ZenStack's own engine built on Kysely, while the ZModel schema stays largely compatible and the query API stays PrismaClient-compatible. Supported databases: PostgreSQL, MySQL, SQLite.
Because V2 was Prisma-based, the migration has two layers: the generic Prisma→ZenStack changes, then
the V2-specific deltas below. For general setup/CLI see zenstack-project-setup.
Step 1 — Do the Prisma migration first
V2 ran on Prisma, so start with the zenstack-migrate-from-prisma skill (swap deps, move the
schema to zenstack/schema.zmodel, replace the client with ZenStackClient, update generate/migrate
scripts). Then apply the V2-specific steps below.
Step 2 — Rename ZenStack packages
npm uninstall zenstack @zenstackhq/runtime
npm install @zenstackhq/schema @zenstackhq/orm
npm install --save-dev @zenstackhq/cli
| V2 | V3 |
|---|---|
zenstack (CLI) | @zenstackhq/cli |
@zenstackhq/runtime | @zenstackhq/orm |
| — | @zenstackhq/schema (new) |
The CLI command moves from zenstack <cmd> to zen <cmd> (e.g. zen generate).
Step 3 — Access control is now a plugin
In V2 access control was built into the runtime (enhance(prisma)). In V3 it's an opt-in plugin.
- Install it:
npm install @zenstackhq/plugin-policy - Declare it in the schema:
plugin policy { provider = '@zenstackhq/plugin-policy' } - Wrap the client and bind the user per request:
import { ZenStackClient } from '@zenstackhq/orm'; import { PolicyPlugin } from '@zenstackhq/plugin-policy'; export const db = new ZenStackClient(schema, { dialect }); export const authDb = db.$use(new PolicyPlugin()); // was: enhance(prisma, { user }) // per request: const userDb = authDb.$setAuth(user); // was: passing { user } to enhance()
See zenstack-access-control for the full policy/runtime model.
Step 4 — Post-update policies: future() → post-update + before()
V2 expressed post-update conditions with future() inside an update rule. V3 uses a dedicated
post-update operation, where bare field references mean the new values and before() reads the
old ones.
// V2
@@deny('update', future().ownerId != ownerId)
// V3
@@deny('post-update', ownerId != before().ownerId)
Step 5 — Abstract models → types + mixins
V2's abstract model + extends becomes a type applied with with (see
zenstack-schema-modeling).
// V2
abstract model Timestamped {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post extends Timestamped { title String }
// V3
type Timestamped {
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
}
model Post with Timestamped { title String }
(Note: extends still exists in V3, but for polymorphism via @@delegate, which is a different
feature — don't use it as a plain mixin replacement.)
Step 6 — Server adapters
V3 requires you to pass an explicit apiHandler (RPCApiHandler or RestApiHandler), and the
client-supplying callback is renamed getPrisma → getClient:
// V2
ZenStackMiddleware({ getPrisma: (req) => enhance(prisma, { user: getUser(req) }) });
// V3
ZenStackMiddleware({
apiHandler: new RPCApiHandler({ schema }),
getClient: (req) => authDb.$setAuth(getUser(req)),
});
See zenstack-crud-server for all frameworks and both API styles.
Step 7 — Client-side hooks (TanStack Query)
Flat hook names are replaced by hooks grouped under a client that mirrors the ORM:
// V2
import { useFindManyUser } from '~/hooks';
const { data } = useFindManyUser({ where: { ... } });
// V3
import { useClientQueries } from '@zenstackhq/tanstack-query/react';
import { schema } from '~/zenstack/schema';
const client = useClientQueries(schema);
const { data } = client.user.useFindMany({ where: { ... } });
SWR support was dropped in V3. See zenstack-crud-server for the full TanStack Query setup.
Step 8 — Other plugin/utility migrations
- Zod: now a utility rather than a plugin (see the zod utility docs).
- OpenAPI: folded into the automatic CRUD API handlers — generate a spec via
apiHandler.generateSpec()(seezenstack-crud-server). - Custom plugins: the V3 plugin system is revised; consult the current plugin docs.
After upgrading
Run zen generate, typecheck, and exercise your test suite / app. Confirm access control behaves as
expected now that it's an explicit $use(new PolicyPlugin()) + $setAuth() flow rather than V2's
implicit enhance().
Reference docs
Full ZenStack documentation for this topic is bundled under references/:
- migrate-v2.md — official "Migrating from ZenStack v2" guide