objection-authorize v4 is finally here, and with it brings a whole host of improvements! As usual, you can read the changelog over on the releases page.
From when I first wrote about it (back in v3) and even from when I first released v1, a lot has changed in terms of the library('s internal workings, because the API is mostly the same even today - just chain
.authorize() to your query!) and the ecosystem (ORMs, AuthZ libraries), and yet the problems we face are the same as ever.
In this blogpost, I'm going to go over those circumstantial changes, and the chain of thoughts that led to v4 and hopefully extrapolate it to see where this might lead.
To Be, or not To Be
Frankly, I really struggled with whether to even write another version of
objection-authorize at all. While it has been my goal to overhaul the internals of the library to use static hooks as soon as Objection.js v2 came out (static hooks offered more flexibility and worked more consistently, even for the most complex queries), I have been hesitant to actually put in the work to make it happen.
For one, between the time I wrote (about) the last version and the current version, I:
- Got a job after almost half a year of unemployment right out of college, right when my visa was about to run out
- Worked full-time for a startup, taking on senior-level duty and expectations with junior-level salary
- Got laid off from said startup due to the pandemic
- Had to look for a job for another 3 months, again, almost running out of visa
- Was dragged to the mental ward by the cops, and got in $3,000 of medical debt due to previous company's health insurance running out (and of course I wasn't allowed to seek medicare/medicaid/the exchange for help since I'm a "foreigner")
- Got a full-time job with a (hopefully) more stable company, again working and training full-time
But the personal burdens aside, I seriously began to doubt whether isomorphism (which is the raison d'être of this library) was even worth it. After all, for how stupidly complex the library was (the lines of code really do not do it justice - there's so much complexity that the library has to handle on behalf of the users), just how much benefit does it actually provide?
Isomorphism seems like a simple, niche thing, and if you think about today's typical production (i.e. no "hello world"s) web applications, 90% of the backend is doing its backend business logic magic, and 90% of the frontend is doing its frontend UI logic magic. Only about 10% (if that) is dedicated to interfacing with each other, and about half of that is schema, and the other half is access control.
In addition, for many applications, there is an additional layer of abstraction across the frontend and the backend (the authentication service/SSO which handles RBAC), further reducing the need (and increasing the complexity) for isomorphism.
So, why bother?
I first started working on this library after I experienced MASSIVE pain trying to coordinate the frontend and the backend (not just the codebase, but also the team) together during my final project at college. In particular, the frontend devs simply couldn't integrate the service endpoints well, because they were basically reinventing backend logic in the frontend.
And I saw the same thing happen again in my first company, Clinc (to an even more extreme degree), but again, I brushed it off to incompetent engineers (not saying frontend engineers are incompetent, but that Clinc's was)/lack of communication and coordination between the different teams: aka, culture.
But as I was working for my current company, I couldn't say my current team had the same kind of cultural problems, even though we were still facing similar problems (of having to write logic on the backend, and write additional logic on the frontend to integrate the backend).
The backend and the frontend were well-architected, the overlap of logic between those two were kept to a minimum, and there were predefined OpenAPI schema to interface between the two.
But it still wasn't enough.
Every time someone wanted to make any sort of modifications to the 10% that overlapped (or add/modify endpoints), they would have to touch every part of the stack (remember - it's not "just" frontend and backend for most production systems - you're going to have multiple services and frontend components all touching each other) just to make that change, and it was clearly dragging down developer productivity, not to mention correctness.
Why should you have to change 2 parts or more when you're really just changing one piece of logic?
So this is why isomorphism still matters, even for complex applications - because you don't want the 10% of your application which is interfacing with each other to be the bottleneck during development.
And, speaking of bottlenecks:
Remember how literally my last blogpost was about bus factors in open source? We all have excuses (see mine above), but clearly, taking a year in-between releases is a prime example of what not to do, of "the bus factor problem" in effect.
In addition, part of the pain point of developing
objection-authorize over the years was supporting the various authorization libraries (thankfully, I lucked out this time during development of v4 as one of the libraries actually got dropped by its developer and was archived/deprecated - still, this serves as a reminder that my investment into making pluggable interfaces that would serve all sorts of authorization libraries was worth it).
Simply put, the more authorization libraries I need to support, the bigger the surface area gets, and the more time and breadth of knowledge it takes to maintain the library, the more susceptible it is to bugs, the more complex the internals get.
And of course, the internals would need to be designed in a way that handles the "WTF" factors of each authorization libraries (for example, a huge part of why maintaining v3 was such a pain in the ass was because
casl did not have any "field negations", i.e. it did not know which fields to exclude, only which fields to include, while the other supported library -
Furthermore, as my projects based on
objection-authorize expand (and hopefully more and more people start to seriously evaluate this library for their own use), the use cases would also explode exponentially - and that means the need to cover lots of more queries and edge cases.
The move to static hooks should cover a lot of them, but I expect little corner cases to arise when you try to use it for something like complex graph upserts running behind a GraphQL API, for example (of course, the power of this library lies in the ability to just bypass all the "magic" that it provides you and dip down to the ACL level directly - just like how the strength of Objection.js is the ability to go down to the query builder level directly when you need to construct complex SQL queries that the ORM just can't cover).
So, with expanding queries, use cases, and library support (not to mention the decreasing free time with my full-time job), how am I to handle all of this?
Well, I don't.
As mentioned in the previous blogpost about bus factors, the "endgame" for open source libraries is either widespread community support or paid commercial support.
And for obvious reasons, the latter approach is just completely out of the realm of possibilities for me. So that leaves only one option - but how?
To get more than just eyeballs on the library (remember, the thing that really matters in terms of community support, is not the number of stars or npm downloads or the "popularity" - it's ensuring that as many people as possible have a stake in the game so that they'll be motivated to support the library for their own benefits), we need to do a couple of things:
However, as the middleman, we can draw in people from both communities: for developers who use the ORM but not predefined ACLs, this library can be the gateway drug for drastically simplifying their backend code using ACL libraries, at first (they don't even need to care about isomorphism). For developers who use predefined ACLs (say, for the frontend) but not the ORM, this library can be the gateway drug for a "plug-and-play" approach to the backend!
Second, we need to expand our reach to make that "gateway drug approach" work best: and that means serving as the middleman for more communities - for now, this approach only combines Objection and CASL communities, which are both very niche. Obviously this library won't reach breakout celebrity status by adopting some crazy popular libraries, because ORMs and ACLs in the node space is already niche to begin with.
But, with the pluggable interface, the hope is that we will be able reach out to one community at a time, starting with support for different authorization libraries (I'll have to check which ones are popular and actively used). And one by one, hopefully more people from those communities will be able to maintain the adapters specific to their libraries (leaving me to only maintain the core interface tying all of them together).
This is going to be a serious uphill battle. Like I've said, 99% of open source code just dies because of lack of community adoption. And despite some people expressing interest for this library (if github stars and npm downloads are anything to go by), I'm pretty sure I'm the only one who's seriously using this in their applications.
It's going to be hard, but this is the only way as far as I can see. Otherwise, this library will up wither and die like the rest, and so will the key piece in achieving true isomprhism for web apps.