Hello guys,
I’m the creator of the ZenStack project. I had the chance to meet with some wonderful folks from the Redwood team at the RedwoodJS Conf back in Sept. There I also gave a quick demo to Tom and Rob (@rob I believe ) about how it feels to model complex authorization using the toolkit. As we’ve released several more iterations since then, and the project is in a stable V1 now, I think it makes sense to propose an integration guide into Redwood docs now.
Just a quick recap about ZenStack. It uses an extended Prisma Schema Language to model data and access policies together. It allows you to model RBAC, ABAC, or mixed. For example:
model Post {
author User @relation(fields: [authorId], references: [id])
authorId Int
published Boolean
@@allow('read', published) // everyone can read published posts
@@allow('all', author == auth()) // author has full access
}
At runtime, you create an enhanced PrismaClient, which automatically enforces the policies. It can be used as a drop-in replacement to the regular PrismaClient.
// `db` has the same typing as `prisma`
const db = enhance(prisma, { user: context.currentUser });
Before starting to work on a “How To” PR, I wanted to first check if the team likes the idea and, if so, whether the integration approach that I’m thinking of makes sense.
Here are the current thoughts in my head:
Set up
- ZenStack provides a
@zenstackhq/redwood
package for supporting project setup and runtime. - Setup should be as easy as running
npx @zenstackhq/redwood
oryarn rw setup package @zenstackhq/redwood
. I’m not sure if the latter is the preferred method. - What the setup does is:
- Install yarn dependencies
- Copy “db/schema.prisma” to “db/schema.model” (ZModel is the extended PSL)
- Install a GraphQLYoga plugin like:
The// api/src/functions/graphql.ts export const handler = createGraphQLHandler({ ... extraPlugins: [useZenStack(db)] });
useZenStack
plugin creates an enhanced PrismaClient for the current user and adds it into the GraphQL context ascontext.db
.
Usage
For developers, only two main differences:
- Instead of modeling in
db/schema.prisma
, do it indb/schema.zmodel
. Then runnpx zenstack generate
andschema.prisma
will be regenerated (together with other code for supporting access policy enforcement). All Prisma workflows, like migration generation, db push, etc. stay unchanged. - Instead of using
db
in GQL services, usecontext.db
instead for access policy enforcement.export const posts = () => { return context.db.post.findMany() }
Btw, we should probably wrap zenstack generate
into yarn rw zenstack generate
. My understanding is that the CLI commands are not pluggable so it’ll require changes to the Redwood code base. Right?
Other notes
Developers can continue using the @requireAuth
to guard the GQL queries and mutations, but it’s optional if the ZModel has fully expressed authorization requirements.
The Redwood RBAC can be used together with ZenStack. Developers can still use “roles” to guard frontend routes, and they can also use “role” to define policies in ZModel.
Demo project
You can find a fully implemented multi-tenant Todo app here.
Please let me know if you feel the proposal aligns with the Redwood team’s plan for the documentation. And I’d appreciate any suggestions for how to make the integration more frictionless.
I’ll be happy to make a PR if you think it’s the way to go! Thanks!