What it is
ZenStack is a Node.js/TypeScript toolkit that simplifies the development of a web app's backend. It supercharges Prisma ORM with a powerful access control layer and unleashes its full potential for full-stack development.
Our goal is to let you save time writing boilerplate code and focus on building real features!
How it works
Read full documentation at 👉🏻 zenstack.dev. Join Discord for feedback and questions.
ZenStack incrementally extends Prisma's power with the following four layers:
1. ZModel - an extended Prisma schema language
ZenStack introduces a data modeling language called "ZModel" - a superset of Prisma schema language. It extended Prisma schema with custom attributes and functions and, based on that, implemented a flexible access control layer around Prisma.
// schema.zmodel
model Post {
id String @id
title String
published Boolean @default(false)
author User @relation(fields: [authorId], references: [id])
authorId String
// 🔐 allow logged-in users to read published posts
@@allow('read', auth() != null && published)
// 🔐 allow full CRUD by author
@@allow('all', author == auth())
}
The zenstack
CLI transpiles the ZModel into a standard Prisma schema, which you can use with the regular Prisma workflows.
2. Runtime enhancements to Prisma client
At runtime, transparent proxies are created around Prisma clients for intercepting queries and mutations to enforce access policies.
import { enhance } from '@zenstackhq/runtime';
const prisma = new PrismaClient();
async function getPosts(userId: string) {
const enhanced = enhance(prisma, { user: userId });
return enhanced.post.findMany();
}
3. Automatic RESTful APIs through server adapters
Server adapter packages help you wrap an access-control-enabled Prisma client into backend CRUD APIs that can be safely called from the frontend. Here's an example for Next.js:
import { requestHandler } from '@zenstackhq/next';
import { enhance } from '@zenstackhq/runtime';
import { getSessionUser } from '@lib/auth';
import { prisma } from '@lib/db';
export default requestHandler({
getPrisma: (req, res) => enhance(prisma, { user: getSessionUser(req, res) }),
});
4. Generated client libraries (hooks) for data access
Plugins can generate strong-typed client libraries that talk to the aforementioned APIs. Here's an example for React:
import { useFindManyPost } from '@lib/hooks';
const MyPosts = () => {
const { data: posts } = useFindManyPost({
include: { author: true },
orderBy: { createdAt: 'desc' },
});
return (
<ul>
{posts?.map((post) => (
<li key={post.id}>
{post.title} by {post.author.name}
</li>
))}
</ul>
);
};
Architecture
The following diagram gives a high-level architecture overview of ZenStack.
Links
Features
- Access control and data validation rules right inside your Prisma schema
- Auto-generated OpenAPI (RESTful) specifications, services, and client libraries
- End-to-end type safety
- Extensible: custom attributes, functions, and a plugin system
- A framework-agnostic core with framework-specific adapters
- Uncompromised performance
Plugins
Framework adapters
Prisma schema extensions
Examples
Todo SaaS App
Check out the Collaborative Todo App for a running example. You can find different implementations below:
Blog App
Join our discord server for chat and updates!
Contributing
If you like ZenStack, join us to make it a better tool! Please use the Contributing Guide for details on how to get started, and don't hesitate to join Discord to share your thoughts.
License
MIT