Skip to main content

Stories Behind ZenStack V2

· 6 min read
Jiasheng
Co-founder of ZenStack

Cover Image

After polishing ZenStack V2 in the future branch for more than two months, we are happy to make it official now. I would like to take this opportunity to briefly show you the main features of V2 and the stories behind it.

Polymorphic Relations

This feature is actually the reason we created V2. It's one of the most desired features of Prisma, which you can see from the reactions to the two related GitHub issues:

Some folks even think that an ORM is incomplete without polymorphism support.

As the believer and enhancer of Prisma, one of our strategies is to pick up where Prisma has left. So, it did come to our radar at the early stage:

Support for Polymorphic Associations #430

It quickly became one of the most desired features of ZenStack too. Not only for the reactions themselves but also because more issues actually depend on it:

However, we know it’s a non-trivial task. Therefore, instead of rushing into the implementation, we wrote a blog post first to explain our approach and send it to the communities of both Prisma and ZenStack to get their feedback:

Tackling Polymorphism in Prisma

The feedback has exceeded our expectations. One guy even refers to it as Christmas magic:

github-comments

Gaining enough confidence, we began working on the implementation. Our confidence was further boosted when Prisma mentioned it as a third-party solution in its official blog, even though the feature was still in the alpha stage:

Table inheritance | Prisma Documentation

Now, you can fully enjoy it with the latest version of ZenStack:

Polymorphic Relations

Why should you use it? Check out the below post to illustrate the benefit with a real example:

End-To-End Polymorphism: From Database to UI, Achieving SOLID Design

Edge Support

This is a surprising gift we received from Prisma. Previously, when a user asked if ZenStack could run on Edge, we would direct them to the Prisma Accelerate proxy, as it was the only way to do it. However, not all users are willing to use another service. This situation is frustrating for us because, unlike resolving polymorphism, we can't fully address it from ZenStack unless reimplementing the entire Prisma engine.

Fortunately, Prisma sent us a fantastic gift at the start of 2024. Without hesitation, we began tweaking ZenStack to ensure its compatibility with the edge runtime. Even though the workload is minimal compared to polymorphism, we gained substantial knowledge throughout the process. If you're also planning to adapt to the Edge runtime, we hope our experience can save you some time:

Adapting ZenStack to the Edge: Our Struggles and Learnings

By the way, Edge support is in preview for both Prisma and ZenStack, so if you run into any issues, feel free to contact us via Twitter, Discord, or GitHub.

Auth() in Default()

This is a very special feature. The brilliant idea came from one of our users at a very early time:

Support for auth() in @default annotation #310

The best part is that right before we started to consider whether to implement it in V2, another user sent out a PR for it:

Support for auth() in @default attribute #958

This is the first feature of ZenStack that is fully implemented by the community. We would like to express our gratitude by sharing the author’s GitHub profile link here.

It once again shows the benefit of using declarative schema to reduce the duplication of imperative code:

  • before
    //schema.zmodel
    model Post {
    ...
    owner User @relation(fields: [ownerId], references: [id])
    ownerId Int
    }

    //xxx.ts
    const db = enhance(prisma, { user });
    await db.post.create({
    data: {
    owner: { connect: { id: user.id } },
    title: 'Post1'
    }
    })
  • after
    //schema.zmodel
    model Post {
    ...
    owner User @relation(fields: [ownerId], references: [id])
    ownerId Int @default(auth().id) // <- assign ownerId automatically
    }

    //xxx.ts
    const db = enhance(prisma, { user });
    await db.post.create({ data: { title: 'Post1' } });

VSCode Auto-Formatting

This is the feature I regret the most. In fact, I would call it a fix instead of a feature because I did implement it in V1:

twitter-link

I mentioned “minimize disruptions for prisma users”, but you know what? It was actually a lie. Although I tried to make ZModel look as same as Prisma, it used a different indentation style:

style-compare

Which one do you think looks better? 😉 I convinced myself that the left one was better due to its consistency with TS style. Why need consistency with TS? Because it includes the access policy code.

It turns out that all of that was just an excuse to avoid making changes. Even when I realized it soon, I found another excuse that the change would disturb existing users. You can see how skilled our mind is at finding excuses to avoid work. However, it struggles to estimate the actual workload accurately because it attempts to avoid it.

The total time I spent to implement it is just a couple of hours which even includes flags both in VSCode extension and CLI to give the user the option to preserve the old style if he likes:

vscode-option

Usage: zenstack format [options]

Format a ZenStack schema file.

Options:
--no-prisma-style do not use prisma style

Don't let inertia obscure the truth; use your heart to find the right path and pursue it.

Final Words

You can find the complete change detail in the below post:

Upgrading to V2

In the Game of Thrones series finale, Tyrion Lannister said the following words:

What unites people? Armies? Gold? Flags? Stories. There's nothing in the world more powerful than a good story.

These features will probably be forgotten as time passes or even not used at all by most people. But I hope the stories behind them could still have some meaning for you.

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