| great example. Some of this is out of my bailiwick, but on a high level I agree with you. I think your intuition is right. If you have behavior that's dependent on priority, this is a code-smell. It feels like you're just sort of #yolo'ing and hoping the right resolver is called. So far.. In these situations I usually pause and reconsider my architecture. There are probably several solutions here. (Do bear in mind that I'm still learning the Pathom kungfu here, so I can't guarantee these are the best solutions..) 1. So in your example the first step in isolating the behavior would be to re-think of it as three keys - ::employee-ID - ::company-ID - ::employee-company-id-pair and make more resolvers - ::employee-ID -> ::employee - ::company-ID -> ::company - ::employee-ID + ::company-ID -> ::employee-company-ID-pair - ::employee-company-ID-pair -> ::employee-company-pair You can then just request an ::employee-company-pair and it should be disambigious. The problem is that you've now have a dense pair that doesn't hook back up with the rest of your resolvers. But this can be addressed with ... 2. isolating behavior using "nested inputs/outputs". They allow you to go from a soup of keys to a narrow subset Again: - ::employee-ID - ::company-ID - ::employee-company-id-pair first you can just have the original three 1-to-1 resolvers - ::employee-ID -> ::employee - ::company-ID -> ::company - ::employee-company-ID-pair -> ::employee + ::company At this point, as you illustrated, you have a bit of a priority issue.
With a ::employee-ID and ::company-ID keys it's unclear which path is taken. The trick is to now use nested input to disambiguate things. You make a resolver that returns the results wrapped in a key (nested output): - ::employee-ID + ::company-ID -> {::packed-request [::employee-company-ID-pair]} The "consumer" resolver that only wants that efficient db call has on input a ::packed-request and just "unpacks" the request using nested inputs. Furthermore on input it will directly requests {::packed-request [::employee ::company]} and the engine handles the ::employee-company-ID-pair -> ::employee + ::company conversion. This nested input scope (ie. the inside of ::packed-request) doesn't have ::employee-ID and ::company-ID keys, so the request is always unambiguous. The Pathom docs could be a bit more clear on this. They just show the basics and unfortunately don't walk through these tricks. But you can be explicit about both input and output map shapes and the engine uses these to do conversions. This allows you to narrow the set of inputs. So here one resolver outputs a {:packed-request [::employee-company-ID-pair]} and another takes a {::packed-request [::employee ::company]} - and the conversion is implicit. The engine finds only one resolver that returns a ::packed-request and it sees that it internally it will have a ::employee-company-ID-pair key. It then looks for a path from ::employee-company-ID-pair to the requested ::employee + ::company pair and it finds the corresponding resolver(s). Sometimes you need to forward other keys into this inner context, but you just provide them in parallel to the ::employee-company-ID-pair - it's all explicit. I'll admit.. this looks weird. As I said elsewhere.. it's a real paradigm shift in how to think about and organize code. But I find after some adjustment it's actually worked really nicely for me so far. |
If we don't have the users data, we can't pass the company id with the user ID. This is not a problem if you are doing an explicit query because you can fetch user + company data at the same time. But if you're using the three resolvers above and your only way to guarantee the path chosen is to already have the company id, that won't be possible.
Here's another thing I ran into with Pathom. Adding keys may make a previous desired path choice change. Let's say we have our three resolvers return the following.
- GetUser - provides :user/email
- GetCompany - provides :company/phone_number
- GetUserWithCompany - Provides :user/email :company/phone_number
I query the environment by giving it a user ID and asking for the phone number that belongs to that users company.
It pings the third resolver. That's great! It got the information I want in one node.But now what if we also want the users email?
You would think this would just use the same resolver because it provides both of these keys. But it doesn't. It will call the other two resolvers.The reason it does this in my example is I had made a bridge between user and company before I made the more efficient resolver that gets all of that data at once.
We needed this bridge before, otherwise we just had two separate user and company queries that couldn't connect at all. The bridge lets that data be joined. But when the bridge is still here, Pathom, for whatever reason, will get the users data first and use the cached result to get the rest of the data, instead of just using our new resolver that gets it all at once. The only solution here is either to set priorities on the resolvers, or to remember to remove the bridge when adding new resolvers.You might think this is petty and arbitrary, and maybe it is. Maybe I am holding it wrong. But it is exactly the kind of thing I ran into just doing test scripts on my own with very simple schemas. Imagine working with 12 people and having hundreds of tables.
I suppose the nice thing about Pathom is if you really want to override the path, you can just fetch that stuff manually and then query the environment with the data you got manually.