Replies: 6 comments
|
Thanks for the issue. I see your pain in this issue. Sorry to here that. Did you look into casl-examples repo? PS: I could tell you a bunch of things why you should not use nestjs 😁 but it’s a separate topic. Let’s first solve your issue |
0 replies
|
0 replies
|
Elysia/bun really worth looking into! |
0 replies
|
If you use zod, you could use nestjs zod. This is so much better than class-validator and multiple lines of decorated code |
0 replies
|
There are 2 ways to solve ACL on API and FE:
|
0 replies
|
Moved this to discussion, since this is not the issue in casl but just an integration complexity for specific framework and architecture |
0 replies
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
Hello CASL team,
I am writing to report a major architectural conflict and a critical documentation gap when using CASL in a modern, isomorphic TypeScript environment, specifically with a NestJS backend. After spending several days debugging, I've concluded that CASL's current design for subject type detection is fundamentally incompatible with NestJS's best practices, leading to a dead end for developers.
This is a significant Developer Experience (DX) issue that needs to be addressed in your documentation.
The Context & Goal
My goal was to leverage CASL's primary strength: creating a single source of truth for permissions to be used on both a NestJS backend and a web frontend.
The NestJS ecosystem heavily relies on decorator-based classes for DTOs and Entities (using libraries like class-validator, @nestjs/swagger, typeorm, etc.). This is the standard and recommended approach within that framework.
The Fundamental Problem: constructor.name vs. Decorator-based DTOs
The core of the conflict lies in how CASL detects the subject's type. It relies on subject.constructor.name.
NestJS DTOs cannot be shared: The decorator-heavy classes from the NestJS backend cannot be shared with the frontend, as they carry backend-specific dependencies and metadata.
Plain objects/interfaces fail: When the frontend receives data (e.g., from an API), it works with plain JavaScript objects (Object). Any rule check like ability.can('update', articleObject) fails because articleObject.constructor.name returns 'Object', not the expected 'Article'.
This means the entire concept of condition-based, type-safe rules breaks down in a real-world isomorphic application.
Investigated Workarounds and Their Failures
I explored the two possible workarounds, both of which are impractical and lead to severe issues:
Using String-based Subjects (e.g., ability.can('update', 'Article'))
This approach decouples the rule from the object, but introduces a TypeScript nightmare. Trying to create a type-safe mapping between the string 'Article' and the Article type clashes with CASL's complex internal generics. It requires developers to build and maintain a fragile layer of custom types just to fight the library's rigid type system.
Using Isomorphic "Plain Classes"
The only other way is to create a shared library of "plain" classes without any decorators. Both the frontend and backend would use these. However, this is a huge architectural compromise that effectively breaks the NestJS ecosystem:
It forces developers to abandon the powerful, integrated DTO pattern of NestJS.
It introduces significant boilerplate, requiring constant mapping between NestJS's "smart" DTOs and CASL's "dumb" plain classes (plainToInstance, etc.).
It complicates the entire development workflow for the sake of satisfying one library's requirement.
Conclusion: A Critical Documentation Gap
The promise that "CASL works on the FE and BE" is misleading. It comes with a huge, unstated architectural requirement: you must design your entire entity architecture around CASL's needs, often in direct opposition to the best practices of your chosen framework.
This is an architectural trap. Without any warning in the documentation, developers following standard NestJS practices will inevitably hit this wall and waste days of valuable development time.
Suggested Actions
To prevent other developers from experiencing this same frustration, I strongly urge you to update the documentation:
(Minimum) Add a Prominent Warning: In the README.md and key documentation pages, add a clear and visible warning about this architectural conflict with decorator-based frameworks like NestJS.
(Recommended) Create a Dedicated Guide: Add a new page under "Recipes" or "Guides" titled "Usage with NestJS" or "Architectural Considerations". This page should honestly explain:
The constructor.name limitation.
The conflict with decorator-based DTOs.
The required "shared plain class" architecture as the only viable (but compromise-heavy) solution.
CASL is a powerful tool for its intended ABAC niche. However, this lack of transparent guidance on a critical integration point is a serious flaw.
Thank you for your time and consideration.
All reactions