AdonisJS Mixin for Fetching Models of Authenticated User
/ 5 min read
Last Updated:For anyone unfamiliar, AdonisJS v6 is a TypeScript-first Node.js web framework, similar to the likes of Laravel and Ruby on Rails. If you’re comfortable with an MVC pattern, you’ll be right at home with AdonisJS.
Fetching Models of Authenticated User
While working on a personal financial tracking app, I realized that querying a Model based on the currently authenticated user would be a common workflow since I won’t be sharing data across users.
I landed on the below, very clean solution:
That’s it!
How I got there
Let’s take a step back and look where I started, and how my code evolved to this solution.
The Basic Way
There’s nothing wrong with this approach, but it’s not very reusable. Now anytime I query a model across the entire app, I’ll have to repeat most of that code.
Using withScopes
Now that I know what code I want to reduce, my first goal is to get it working for just this model. We can worry about a more generic solution for all Models later.
After looking at the Lucid docs, I found withScopes.
This essentially lets us create a static property on the model, that has access to the query
object, and, we can pass custom parameters: the authenticated User! Exactly what we need.
Here’s what it looks like:
Now we can do:
This reduces the total lines of code we’ll have, but still is pretty verbose.
Let’s first take a stab at making this reusable across all Models.
Using TypeScript Mixins
One option is to create an AppModel
class that extends BaseModel
, and has the static belongsToUser
variable on it:
This works fine and isn’t a half-bad solution. However, I’d prefer to follow AdonisJS’s usage of TypeScript Mixins.
What is a Mixin?
Mixins are a TypeScript feature for extending the functionality of a class.
AdonisJS uses them in a few ways, one example being in their Official @adonisjs/auth package:
withAuthFinder
adds a few helper methods to add user lookup and password verification methods on a model.
Using the compose helper, we can have User
extend both BaseModel
and AuthFinder
.
Let’s make our own Mixin
Which we can then use like so:
Now any model can easily use the belongsToUser
scope!
Making it shorter
Remember the end goal is to be able to do Transaction.authedQuery()
.
So, let’s start by making a static method within the mixin:
Now, we need to figure out how to get:
- The
query
object - The current authenticated
User
The query is easy, after all, we’re in a class extending BaseModel
, so we can just use this.query()
:
Then, we can use the scope we previously added to the mixin:
I guess we’ll have to suffer with passing the User
object as a parameter for now:
So now, we can do:
Huzzah! almost there. One could argue this is good enough, but that one line will haunt my dreams if I don’t get rid of it.
Accessing HttpContext anywhere
I remembered reading in the docs about a way to get the current HttpContext
from anywhere in the app, via Async local storage.
There’s pros and cons to enabling this, so if you’d rather not, feel free to skip this section.
From the docs:
[Async Local Storage] allows storing data throughout the lifetime of a web request or any other asynchronous duration. It is similar to thread-local storage in other languages
Enabling it is easy:
and now, “anywhere” in the app, we can do:
Combining it all
Taking everything we’ve learned so far and putting it together, we can create a mixin that adds an authedQuery
method to any model:
and use it like so 🎉: