Skip to main content
Version: 3.0 Alpha

Overview

ZenStack ORM is a schema-first ORM for modern TypeScript applications. It learnt from the prior arts and aims to provide an awesome developer experience by combining the best ingredients into a cohesive package.

Key Features

Prisma-Compatible Schema Language

The schema language used by ZenStack (called ZModel) is a superset of Prisma schema language. Every valid Prisma schema can be used with ZenStack unchanged. In addition, ZModel added many important extensions to the language to improve the developer experience and enable advanced features.

model User {
id String @id @default(cuid())
email String @unique
posts Post[]
}

model Post {
id String @id @default(cuid())
title String @length(1, 256)
author User @relation(fields: [authorId], references: [id])
authorId String
}

Prisma-Compatible Query API

Although ZenStack has a completely different implementation (based on Kysely), it replicated Prisma ORM's query API so that you can use it pretty much as a drop-in replacement. Even if you're not a Prisma user, the query API is very intuitive and easy to learn.

await db.user.findMany({
where: {
email: {
endsWith: 'zenstack.dev',
},
},
orderBy: {
createdAt: 'desc',
},
include: { posts: true }
});

Low-Level Query-Builder Powered by Kysely

ORM APIs are concise and pleasant, but they have their limitations. When you need extra power, you can fall back to the low-level query builder API powered by Kysely - limitless expressiveness, full type safety, and zero extra setup.

await db.$qb
.selectFrom('User')
.leftJoin('Post', 'Post.authorId', 'User.id')
.select(['User.id', 'User.email', 'Post.title'])
.execute();

Access Control

ZenStack ORM comes with a powerful built-in access control system. You can define access rules right inside the schema. The rules are enforced at runtime via query injection, so it doesn't rely on any database specific row-level security features.

model Post {
id String @id @default(cuid())
title. String @length(1, 256)
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String

// no anonymous access
@@deny('all', auth() == null)

// author has full access
@@allow('all', authorId == auth().id)

// published posts are readable by anyone
@@allow('read', published)
}

Polymorphic Models

Real-world applications often involves storing polymorphic data which is notoriously complex to model and query. ZenStack does the heavy-lifting for you so you can model an inheritance hierarchy with simple annotations, and query them with perfect type safety.

model Asset {
id String @id @default(cuid())
title String @length(1, 256)
type String

// the ORM uses the `type` field to determine to which concrete model
// a query should be delegated
@@delegate(type)
}

model Post extends Asset {
content String
}
const asset = await db.asset.findFirst();
if (asset.type === 'Post') {
// asset's type is narrowed down to `Post`
console.log(asset.content);
} else {
// other asset type
}

Straightforward and Light-Weighted

Compared to Prisma and previous versions of ZenStack, v3 is more straightforward and light-weighted.

  • No runtime dependency to Prisma, thus no overhead of Rust/WASM query engines.
  • No magic generating into node_modules. You fully control how the generated code is compiled and bundled.
  • Less code generation, more type inference.
Comments
Feel free to ask questions, give feedback, or report issues.

Don't Spam


You can edit/delete your comments by going directly to the discussion, clicking on the 'comments' link below