One thing that you’ll run into when interfacing with complex javascript objects in Reason, is that dealing with optional objects can be a huge pain. The most common place to run into this is when using the excellent graphql-ppx syntax plugin (often together with reason-apollo), because the result of a graphql query is a large complex javascript object, with many parts of it that are optional.
The problem
For example, say you have the following query:
query MyAwesomeQuery {
user {
bestFriend {
profilePicture {
url
}
}
}
}
Where everything except the final url
is optional.
The response type then looks like:
type queryResponse = {.
"user": option({.
"bestFriend": option({.
"profilePicture": option({.
"url": string
}),
}),
}),
};
If you want to get the best friend’s profile URL, it’s quite a bit of code:
let url = switch (response##user) {
| None => None
| Some(user) => switch (user##bestFriend) {
| None => None
| Some(bestFriend) => switch (bestFriend##profilePicture) {
| None => None
| Some(profilePicture) => Some(profilePicture##url)
}
}
}
And this might be OK to write once, but it really adds up if you’re doing lots of graphql queries in your app.
A solution!
Gregiorevda, an active community member and core reason constributor, proposed a modification to the reason parser that would add “safe call operators” to address exactly this use case. It’s had a fair amount of discussion back and forth, and I personally was hesitant about making a syntax change like this without first thoroughly testing it out.
And so I made a PPX to test it out! You can install https://github.com/jaredly/get_in_ppx
and experience the magic today.
This ppx includes two operators, that are only valid within the [%get_in ]
form.
#??
is to be used when both sides are optional. E.g. the object on the left is optional, and the attribute you’re getting out is also optional.option({. "attr": option(int)})
#?
is to be used when only the object is optional, but the attribute you’re getting out is not. e.g.option({. "attr": int})
For the example query above, you could do:
let url = [%get_in response##user#??bestFriend#??profilePicture#?url]
Installation
npm install get_in_ppx
- add this line to your bsconfig.json:
"ppx-flags": ["get_in_ppx/ppx"]
And you should be good to go! If you run into any issues, leave me some feedback on the github repo.