elm-gql with Matthew Griffith

Matthew Griffith joins us to share his new type-safe GraphQL tool, and to compare the query builder and query generator approaches.
November 21, 2022


Hello, Jeroen.
Hello, Dillon.
You know what's starting to become almost as common as a Martin sighting on Elm Radio?
Are you talking about Matt's?
I am talking about our good friend Matt Griffith, who is back with us again today to talk about
Elm GQL.
Matt, thanks for coming back.
I'm glad to be back.
This is always a lot of fun.
I hope your sleep has been more peaceful recently and your generation nightmares have subsided.
Yeah, you know, largely.
I feel pretty good.
Thank you.
Thank you.
So now we're going from nightmares to epic battles between two GraphQL tool creators.
I wonder how epic this, how much of a fight this will be.
Fight of the century.
Yeah, I mean, mutual respect, you know, I think that's where it is.
Like, I don't know if it's, yeah.
No, I think it's a really interesting area.
So and I think we have some fundamentally different approaches, right?
And that's cool to talk about.
Yeah, absolutely.
So we've got two different approaches.
We've got Query Builder and Query Generator, which we'll get into.
Elm GQL is a query generator library.
So what is Elm GQL?
Tell us about it.
So Elm GQL, this is under the namespace of vendor inc slash Elm GQL, and the main approach
of the library is that you point it at a GraphQL schema and you point out a GraphQL schema.
It pulls down, like it introspects the schema, and then maybe you write some queries in your,
in like a local GraphQL file, some queries or mutations.
And when you run Elm GQL, the command line tool, it will read those queries or mutations.
So it's basically just a simple command line tool.
So you write a query, you write a query, and then you point it at a GraphQL schema.
It will make sure that there are no collisions in as far as like fields that basically might
need to be aliased.
And if everything, it'll either give you one of those Elm like nice messages, or just saying,
you know, maybe your declared variables aren't quite correct or like you're selecting on
fields that don't exist.
Use that file to ask for that data or run that mutation.
So this is a CLI that generates, that takes GraphQL schemas in GraphQL queries, enters
them into Elm code, which you can then import and start using as you see fits, right?
That's right.
It's not a Elm package at all.
It's just a CLI.
It's actually, yeah, it's actually not.
I realized that there wasn't a whole heck of a lot that I needed to actually be in a
Like the actual API that is common across GraphQL APIs is not very big.
So like an example being like, oh yeah, you can map the result or whatever.
So instead of publishing a package that was maybe only a few functions to sort of help
you out, it just generates that file for you when it generates stuff.
That sounds good.
So if you were to try to workflow, like if someone were to try this out, the first thing
they would probably do would be to, you know, install the NPM package, coin it at a GraphQL
endpoint or a JSON file for a GraphQL schema.
That's right.
And then they would probably go to, you know, some GraphQL playground or some, you know,
insomnia or some sort of tool for playing around with GraphQL APIs.
There are a lot of nice tools because it's this like typed schema and it's a very tooling
focused community.
You use all these nice tools to auto complete out a GraphQL query in one of these tools,
execute it against the API, looks good.
And then you copy paste that string into a.gql file in your, I believe in your source
Yeah, that's right.
So generally my flow for writing these GraphQL files, I was using sort of insomnia, which
has a little GraphQL plugin for a while, but I found in VS code, you can just install a
good GraphQL plugin directly in VS code and then just create the file and you get the,
you know, the big thing, yeah, like you mentioned is the auto complete or like little squigglies
of a field is marked as deprecated.
You can sort of see like, oh, this was deprecated because of, and then when you say something's
deprecated in GraphQL, you give a reason.
So like a little text like, oh, you should be using this thing instead.
And the way code organization kind of works is that let's say you have a file that's called
like, you know, my queries.graphql and you put in a number of queries in there.
What will happen is there will be a folder generated right next to it called my queries.
And within it, there will be an individual Elm file for each query that is listed in
the file.
So what this means is you have two choices that I think could make sense depending on
your, your, your preference.
One of them is it means you can put these things sort of in line.
So like at vendor, we have each page has an API.graphql.
And then so you can go to that page and you can like introspect exactly what are the calls
that it makes and it generates it sort of, I suppose not in line, but basically co located
with the page that's using it.
I think it would be reasonable too, if you wanted to have a top level folder with just
all your graphql and just generate it there.
But this tool would allow you to do either.
So if you have multiple queries in a single GQL file, then it will create a folder with
multiple files.
That's right.
How are these files named?
Do you give names to your?
So there, so in, in graphql, you can add an optional tag, like a name to a query.
So there is a built in idea of a naming in graphql.
So in our case, we want every query to be named.
So you just type in a name for it and it'll use that name to gen to name the Elm file.
Does that mean that you require a name as well?
If you have more than one query, you will need to have a name.
It will default to query if you don't have one, which obviously you could use for like
one, but probably, probably usually you grow out of the one query thing fairly quickly.
So it's really good.
The name is really good for analytics on the backend and pretty common to use.
Actually, why do you create multiple files if you can, if you have different names?
Well, yeah, that's one of the main challenges.
And I know Dillon, you're in this head space as well, is figuring out how to make ergonomic
So the naming rules in Elm GQL, which is basically like if there's a naming scheme for the generated
code for different pieces of it, and it will actually detect if there's going to be a collision
and it has a rule to make a more complicated name, but that won't collide.
And I can kind of get into those, what the, what that looks like.
But cause there was a lot of thought and how do you make that a nice process.
But the reason why not put it in one big file is you increase the likelihood of collisions
and also reading these files.
I wanted it to be very clear when you were reading a given file for a query, what data
is being used as like the arguments going into a query, what data is going to be returned
from the query.
And if you had more than one query in a file, cause sometimes these queries get pretty bonkers
So that there was a high likelihood that it would just be very hard to parse as a human.
And that was one of the goals.
I wanted people to be able to go to these files, like use them as a primary reference
of what's happening and have it be fairly trivial to navigate.
So just to, to make sure we've painted a picture for listeners here, because it's always a
challenge to talk about lots and lots of code in an audio format.
I want to know more about conflicts.
So you take your example query, let's say you're, you know, getting, listing out all
of the GitHub repositories for a particular user.
So for MD Griffith user, maybe you've got a, you know, so you say query all repos for
So that's the name of the query.
And then you, you have parentheses in this GraphQL query syntax where you can declare
variables, GraphQL as a notion of variables.
So you say dollar sign username, colon string bang to say the type.
To say like, this is a required string.
So if you're not familiar with GraphQL, one of the things that's interesting as far as
squaring it with Elm is that it's sort of optional by default required.
You have to have additional annotation and obviously in Elm it's the opposite, right?
Right, right.
In GraphQL, they like to end with a bang.
So when you say variable, you mean in an argument or perhaps.
That's right.
The GraphQL terminology is variable, but you're right that it more intuitively, we would think
of that as a parameter for almost like a function, like the query is like a function and the
variables like a parameter.
So that's a good analogy.
So then now you, you have a GraphQL query that you're writing and in scope, you have
this string, which is the username, and then you just write a regular GraphQL query for
the GitHub API, which is a really nice API.
You can open up the Explorer, we'll drop a link to the GraphQL API Explorer for the GitHub
It's really fun to play with, but then you use, go and make your query for getting all
repositories for the given username.
And then you run a Elm GQL on that copy pasted query that you build up.
And let's say you select, you know, all of the repos and for each repo you get the repo
name and the number of stars.
So now you run Elm GQL.
Elm GQL knows that it knows the types of all these things you're selecting.
It knows the shape of everything basically.
So now you're going to get, you know, like a type.
So in your generated code, so if we, if we called our query, all repos for user, now
you're going to have a file, all repos for user.elm, and you're going to have a type
alias repo that's generated by Elm GQL, and it's going to have named colon string and
star gazers colon into something like that.
So it is, so if you contrast it to like a query builder approach, like, like my own
GraphQL package, you're not writing these type aliases and mapping it into types.
You're writing your query and it gives you all of the type aliases for that.
So, right.
So just to reframe, or not reframe, but just to reiterate, cause it's like the critical
thing is like, okay, Elm vendor Elm GQL is you take GraphQL and you make Elm.
And Dillon Kern's Elm GraphQL is you write Elm code.
You're sort of composing this in Elm and that will generate the GraphQL query.
So the main thing you're interacting with in Dillon's library is the Elm.
And the main thing you're interacting with, at least initially in my library is the GraphQL
And then once you have that, you now have some Elm code, which we had that example of
having a username variable in the GraphQL query.
Now you have that as a parameter in Elm code.
So you call that Elm code.
That's right.
So in my library, the names are so similar.
In vendors GraphQL library, the part you do interact with the generated code, the inputs,
you're still composing in Elm because you can, obviously you want to be able to change
those, like set the date, from what dates do you get the comments or something.
So yeah.
I have to admit that the first time I heard about Elm GQL, I was like, that's a weird
It's just not to conflict with Elm GraphQL from Dillon.
Then I learned that GQL files are a thing.
So then it kind of makes sense.
Naming is tough.
I'm trying to emphasize the author, which I think was the original intent with the literal
naming convention.
So the differentiator is Dillon versus, in this case, it's published under the vendor
namespace for my library.
Although ironically, it's npm install at Dillon current slash Elm GraphQL, and then it's npm
install Elm dash GQL.
I didn't name space.
That's a good piece anyway.
And forget about vendoring the vendor one.
That's just a whole can of worms.
He's shaking his head in shame.
I haven't done anything wrong.
Shame for the podcast.
That you know of.
Okay, cool.
So where do we go from here?
As they say in that Guns N Roses song, where do we go now?
Do the gloves come off now?
I would like to get into it.
Yeah, for sure.
Hopefully people have a basic idea of the fundamental approach and maybe we can go into
what this means.
So the way that I understand this is that if you are used to writing GraphQL queries,
which is likely the case if you have been using GraphQL and haven't used Dillon's version,
then Elm GQL might be more intuitive at least.
And I know that there is some confusion with Dillon's API because it's pretty complex.
It looks like JSON decoders, but even a bit more complicated because you both ask for
things and you map them.
The way I see it, the main difference is that in Dillon's version, it's more complex, but
you can do wrap early, unwrap late.
Basically your version, Matt, is generating primitives.
It's generating type adresses that don't necessarily have guarantees that you...
So it doesn't always generate primitives.
It generates what the GraphQL schema is dictating.
So we're saying that the domain modeling lives in the schema, but it will generate...
So in GraphQL, there's a notion of a union, which maps really nicely to Elm's custom types.
Oh, cool.
These are named.
Are these enums or are these...
So there's a concept of an enum, which maps really nicely to Elm's custom types just without
any data.
Both, I think...
So Dillon and my library, they both handle enums basically the same.
Maybe some trivial differences in naming conventions or whatever, but it's basically like, okay,
It's a custom type.
You can refer to it.
As far as a union, that gets more interesting because in Dillon's library, you have the ability
to sort of, when you're calling a union, mapping that into whatever you want it to be.
So you need to define a type to kind of capture the information and make that decision about
where you're putting it.
And in Elm GQL, you basically would...
You would select for a union, a union being like one of...
It's like this can be one of these five objects, right?
And they all have a concrete name.
And so what Elm GQL will do when you're selecting that, depending on the data you select for
each one, it would generate a custom type where you'd have a variant for each union
that can be selected.
And with the data that you've selected for a given variants on that variant, right?
So you would have a variant or a union type for every query.
That's right.
And this probably gets into one of the interesting bits, which is questions or an exploration
around code reuse or query reuse and sort of type compatibility.
So you can imagine in Dillon's library...
So to frame...
So before I actually jump into this, there's a few things I want to sort of establish.
One at vendor and blissfully before that, we use Dillon's library extensively.
We did that for the last five years or so or four years.
I was only there for three and a half years.
But it was there before me.
And so we had a lot of code that was using Dillon's library.
We still have a lot of code using Dillon's library.
We were just moving over to this new approach.
And one thing that I want to also mention, because I think everyone on this podcast just
like we're so used to it that we don't say it.
Both these libraries have similar guarantees as far as you can't make a query...
You can't ask your API for data that doesn't exist.
So we're both checking against the schema.
Both of these are type safe and have this approach that Dillon talked about in his types
without borders talk about how can you get more type safety outside of just the internal
Elm language.
I just wanted that to be an explicit thing that was said.
So people...
Because sometimes it's like...
I didn't even think about mentioning it.
Yeah, I know.
It's because we're all Elm programmers.
So like, yeah, of course it's type safe.
Like that's not even a question.
And this is a huge benefit from both libraries that we get.
That is probably one of the first motivators.
And if either library didn't have this, it would be significantly less appealing.
But we enjoy this at work where we know the front end cannot ask for...
Cannot compose an incorrect query, at least as regards to the schema.
And cannot ask for data that's incorrect.
And even do things like protect against alias collisions or like field name collisions,
or things like you have to have a...
For variables, right?
You have to declare them correctly and they have to match what's in.
So you say like, I have three variables, their name X, Y, and Z.
And it's like, great.
None of them are in the query.
You're not using them.
That's technically, I think, invalid GraphQL.
So anyway, I wanted to level set a little bit there.
But then, yeah, code reuse, which I think is interesting.
So in Dillon's library, I realize I'm speaking a lot.
So please feel free to interrupt me.
In Dillon's library, you could map a thing into a common type.
In fact, that's sort of the thing you would naturally do, is you'd commonize selecting
for a given union.
And then in Elm GQL, you would have...
These types would be generated separate for each query.
But there is a mechanism that I just added to leverage GraphQL fragments.
Shared fragments.
Okay, cool.
So you wouldn't be able to change the type being returned.
But you could say, if you're using the same fragment, a fragment in this case is like
a subquery.
It's basically like a little named set of fields and data that you're selecting.
The intention with fragments would be that if you're using that fragment, that will generate
the same type in Elm, and those would be handleable.
Where do you define a fragment?
Is it something that you can define in another GQL file or...
Right now it's just in a given query file.
But I do want a notion of a global fragment that's basically like global across your project.
I haven't implemented that yet, but I think it's pretty reasonable.
Okay, there's nothing built in to GQL language.
Right now, no.
I mean, there's a notion of a fragment.
I'm not sure what they would have to add on a language.
Yeah, there's that.
Right, they would have to have imports.
There's definitely no import resolution.
All GraphQL has a notion of is there's this big GQL formatted thing that you send over
to the server and then we run it.
And it can include fragments and whatever, but fragments, queries, that's all they have
the idea of.
So just to paint a picture of what this would look like for anyone who might not know union
types in GraphQL in general.
If you're listing all users in a system and you've got teachers and students, then now
you're going to have to say, well, if it's a teacher, give me this data.
If it's a student, give me this data.
So that's sort of a polymorphic selection set you could call it.
It's just deciding based on the type which fields to select and GraphQL has this awareness
of the types, if it's this type or if it's that other type.
And in the generated Elm code, you would have a corresponding union type in Elm GQL that's
automatically generated because it knows these are the two different types you're selecting.
So I'm going to create a custom type with those two different selections and the subset
of things you've selected for each given one.
So there will be a variant for each.
And Elm GraphQL, my Elm GraphQL library, it's, I mean, and this is sort of like a bit of
the philosophical interesting difference between the vendor approach, your approach and my
approach is the query builder approach that my Elm GraphQL library does.
Everything is very explicit and hand tailored, which means you have full control over the
types you build up.
That also means you have to explicitly write everything that you build up.
You have to explicitly control everything.
So there's more wiring to do to explicitly pull.
And as Jeroen was mentioning earlier, that can be a bit difficult for beginners sometimes
because you have to get comfortable with, oh, I have to map all of these types.
I create a type alias.
Wait a minute is like what happens when I do selection set dot map for some type alias
What is that doing?
And it's like, oh, well it implicitly defines a function that takes four values because
it's a type alias for a record with four values and it's a little bit confusing.
So there are trade offs there, inherent trade offs.
The way I kind of think about it is that Dillon's library allows you to remodel the data coming
in, meaning like you've asked for this data and you can refine it in some sort of way.
So you can say, I want to select these fields and I want to actually create a new abstraction,
something different, a different type.
And this is how I primarily want to interact with this data.
In Elm GQL in my library, I think the thinking is that GraphQL itself has an expressive enough
type system that the design work of the modeling the domain, we want actually to be very close
to what like basically is in the schema.
Like usually we found that there are a few cases where it's like, okay, this wasn't quite
expressed correctly.
Either the schema wasn't designed correctly or there's just this weird invariant we can't
capture in the schema or whatever.
We're also in the headspace of we are in control of our own schema.
And I realized that that's, some people are using talking to other schemas.
And so like for us, it's like, oh yeah, we have opinions about how GraphQL schemas should
be built.
And it's a primary discussion we have a lot at Vendor.
And so for us that modeling idea, we want it to live in the schema as much as possible.
And so, right.
And not sure where we want to go from there.
So does that mean that you pretty much never map whatever you got to a non primitive type?
You never write it?
Again, we're not, I mean, if custom types are primitives, then I guess I can agree,
but like we're generating, it's not, everything's not a Boolean, right?
So it's like we still have the expressive type system and GraphQL that is actually surprisingly
close to Elm except for a few weird nuances.
There's I think a really interesting area that I did not quite, I was worried about
and led me to some interesting observations, which relates directly to what you're talking
about, which is with Elm GQL, I think the meaning you have a GraphQL query, you're generating
probably a decent size set of Elm code or data types to represent that.
The worry is that, oh, I can't map this and you might say like, well, where do I keep,
like the first question is like, where do I keep my UI state?
So like, let's say I needed to have a UI widget.
It was something that required its own like little state.
I think most widgets don't need this, but in some cases you do, right?
Where do I put that?
Usually when we are using the Dillon's approach or building things in Elm, the thing what
we would do is we would stuff that UI state right next to the thing it was operating on.
So maybe you're a few like levels deep and you have a list of stuff and you want to be
able to, like maybe it's a table.
So it's a list of rows.
You want to be able to click into a table and maybe edit some data and capture some
temporary state.
So at the level of the row, we would stuff in some stuff and then we write some code
to traverse, like to go into the code, do the updates in our update statement, right?
Or not statement, sheesh update function.
You'd write some code to go into your data, you know, perform the update and like do the
So it was like, okay, well what is the approach with Elm GQL?
If I can't literally extend the types that are within that thing, what do I do?
The approach that I found to be very valuable, which I think actually Richard has talked
about in some cases, there was a talk at ElmConf that the name of it escapes me.
But usually with GraphQL, you have, this is coming from a database, you usually have primary
Like you have IDs or you may have like in GitHub for a repo, they identify it by like
owner and username, right?
So you have some notion of a primary key.
If you think of your model, just like a little tiny little baby database, that UI information
can live really nicely in a top level dictionary.
And you basically can address things by ID.
And what's interesting is the code you need to write to update that, the update code becomes
much smaller.
Because like all of a sudden you're basically just like, oh, I just, I have the ID because
they clicked on this row, right?
And I can just like update that dictionary by ID and then, and be good.
And it's like, we might be worried about what if that entry doesn't exist, right?
For a given thing.
And I think that there are some interesting strategies to make sure that that's not an
Sometimes with UI state, it can be done sort of just in time.
So usually UI state may not need, usually there's a, for a UI state, there is like the
essential data, which is like, okay, the essential data in this case, maybe we have a person
picker, right?
It's a dropdown picks a person.
So the essential data is from the GraphQL query.
It's the literal person selected.
But the UI state, which is like, is the dropdown open or not?
That has a natural default, like where it's just like, well, it's going to be closed by
And it means you don't actually have to instantiate a bunch of these empty things.
It's just like, oh, well, when they click on this, I'm going to go to that dictionary.
I'm going to say like, they clicked on this ID, open it.
And in dropdowns, it's actually really interesting because dropdown state, you have this potentially
subtle thing.
You only want one dropdown open, except in maybe interesting cases.
So like, you know, I mean, if it's like a nested dropdown is what I'm sort of referring
to, but usually it's like, there's one dropdown.
If there are multiple dropdowns open, that's a problem.
So now addressing things by ID, you naturally get that ability.
There's a very specific place where this can happen.
It's not a dictionary of open dropdowns.
In that case, it's, it's literally a maybe ID of a dropdown that's open.
That's a singular thing.
So there's kind of a natural way to that.
That's how at vendor things started to like, we started to organize things.
I actually really like it.
That's a fascinating insight from, from your experience with that.
It's, it's cool to hear about not only like, you know, this tool that you've created, but
your best practices that you've learned working with it.
That's really interesting.
I like this idea in general a lot.
And by the way, the the Richard Feldman talk you were referencing is called immutable relational
I wonderful.
Yes, that's right.
We'll drop a link in the show notes.
And yeah, I really like this idea of like cross referencing bits of data to derive state
in Elm rather than being sort of feeling like you have to model your data a specific way,
being, being able to say, this is the natural shape of this data.
This is the natural shape of this other data, and I can derive state using both of those
things and I can create some state that references this other state and, and have sort of compound
state by, by cross referencing those two things.
So, so, right.
So like a, a very common pattern, ignoring maybe the interactive UI, but the very common
pattern we have is usually for a given page, there will be a primary list of something,
So it's like the list of whatever.
So in, in GitHub, it's like a list of repos or maybe a list of issues, right?
Or a list of PRs.
And then usually there's also a concept of a detail.
So usually you don't want to select absolutely all the data in your first go around because
you're probably just displaying a table or a list of something.
And if you were to select all the details for all the items, it's sort of, I've sort
of thing it's the antithesis of what GraphQL is good for, which is selecting exactly what
you need so that you can optimize these different things.
So what we do in these cases is you usually have a primary list and that's the query you
run when the page boots, right?
And then when they click on something, we'll have a detail query that's like get the details
for this specific thing.
And then that comes back and usually is shoved into a dictionary, which is mapped like ID
to details.
Would that be at all related to, we had Martin Janacek on to, to talk about, oh yeah.
So Martin called it the Elm store pattern was the term that he used for this idea of
sort of he, he used remote data to model these states of if something is loading or, and
then he, you know, had this way you could sort of say, I'm requesting this data.
So would that be at all related to, would that be at all similar to that pattern?
I think you could definitely use remote data for people who aren't familiar.
Remote data is loading, you know, or have it, or it's been requested, you know, those
different states.
I think, I think it's very amenable to that.
You could have in your details dictionary, it could be a dictionary of an ID to a remote
data of your details.
Sometimes we kind of, this might be, this is probably like messy modeling, but for that
specific case, I know I've done the thing where it's like if it's not in there, I assume
it's loading.
That's probably not totally correct, but there, there is the case of like, well, the, the,
you know, the idea lookup failed, which you have to handle that case anyway.
So right now I think we're using sort of an implicit remote data, even though that's,
that may not be ideal, but I think it's amenable to however you want to model that part.
I think for sure.
So, so one other thing, looking at this comparison between how you, how you work with types between
Elm GQL and Elm GraphQL.
One thing we haven't touched on is custom scalers in GraphQL.
So custom scalers is this really cool tool where you can give names to types other than
string bool int in GraphQL.
So you can say, you know, you could say this is a username.
You can say this is, this is a time, you know, this is a date stamp.
This is a timestamp.
This is a.
Is that equivalent to a type address in Elm or can you do a.
It's not really, you can do more powerful things.
So I think actually Dillon and I, our libraries are actually very similar in this, how we
handle scalers.
So what happens is there are a set of primitives, right?
So float int boolean string that are just there and those decode to primitives, but
the schema can declare custom scalers like, like Dillon mentioned.
And what the client side libraries like Dillon's library and my library, I guess mine's a tool,
whatever is you declare like, so when you start up a Elm GQL project, it generates
a file for you where you can't, which you have to own.
So it generates it first, like once, and then you have to kind of like adjust it and own
But what you can do is you can say like, okay, if you get a timestamp scalar, and we know
all these scalers statically from the schema, get a timestamp.
I know that this is like an ISO.
What is it?
I just made the numbers.
I got the 01.
It's been a long time since we've mentioned those.
Yeah, it's 48.
I wonder what that is.
So you know, you can say, okay, I want to parse that comes down as a string.
Custom scalers, I believe always are backed by strings.
I'm not sure if that's true.
I think you can technically send arbitrary JSON with them, which gets a little wonky.
That does get a little wonky.
So well, let's for the sake of argument, assume that this comes down as a string and what
you can do, and both of our libraries do this, you would define in Elm a decoder and an encoder
to basically say like, this is how I could turn it into something that we want to use
like in Elm, it'd be a time posix or maybe a different like data type if you wanted.
So you just have to add the decoder and encoder part.
And then anything of that type.
Or even just wrapping it.
There's another primitive called ID, which is generally a string, but we keep it's an
ID type, right?
Which we keep as a separate thing from like string.
That's the kind of thing that I was thinking of.
Those things you would like to put them into a separate type just to not confuse.
So you could, as long as it's differentiated in the schema, in the GraphQL schema is like,
these are separate scalers, then on the Elm side, on both of these libraries, you could
do whatever you wanted.
So it really helps to have full control over the schema then.
I mean, yeah.
What does happen sometimes is when you have GraphQL schemas that are designed for non
typed consumers, you get number one, a lot of maybes, a lot of nullable types.
You get nullable lists of nullable strings.
That's very common.
They show up.
Nullable lists of nullable strings.
We probably in our schema, we definitely push the backend developers through like, can we
just make this require, can you just remove the null here?
Because it's technically expressible, but it's never meaningful.
So that happens a lot.
And then custom scalers are heavily underused because you don't really necessarily gain
a lot from them if you don't have a type aware consumer.
That's right.
But if you control your schema, then it is so valuable to use those custom scalers in
your schema, in your GraphQL schema.
Basically the way I think about, and I think we talked about this on our Elm GraphQL episode
a while back, but the way I think about custom scalers in GraphQL is it's a contract.
It's a promise that this conforms to this specification, ISO 8601.
And it's like, how do you trust the contract?
Well, because usually like a GraphQL framework, like a backend framework for GraphQL, if it
knows that a value is type date time, then it's going to use the same serialization and
deserialization function to manage incoming inputs of that type and to serialize outgoing
data of that type.
And so you can sort of trust that it's going through this pinch point where it's treating
it through the same logic.
So it doesn't mean you can't have bugs where you incorrectly serialize or deserialize it,
but it's going through one pinch point.
So it's a really valuable tool.
It's interesting.
I've definitely had thoughts about how to take it, especially when you control the schema,
to take it maybe farther than I think the average schema.
Like an example is usually IDs, like we sort of say like, oh, all IDs are the same.
It's like, well, they're not.
You can use an ID for one thing, for another thing, and then it doesn't make any sense.
So one thing I've thought about is like, okay, well, is this a user ID or is this a, you
know, you have the idea of a flavored ID, right?
So basically you flavor it by the entity or the encoding of it.
And I think that doesn't show up as much specifically because people aren't using things like Elm
as a consumer.
But in Elm, it's actually great because it means like you could have the guarantee like,
oh, this mutation takes a user ID and like you can get that user ID from, you know, this
It's like, great.
There's some immediate additional type safety.
So I realized we're going, but I wanted to, I don't think we talked about this, but one
of the primary motivators for Elm GQL and why we leaned into the GraphQL approach was
we have, you know, we have a really awesome set of Elm developers at vendor.
It's super exciting.
We're also hiring, just so you know, but we also have a lot of developers on the backend
with a lot of different experience.
Some of them jump into the Elm code pretty easily.
Some of them are farther back, I suppose.
I don't know, but what we found was very valuable is just if someone comes to you and asks you
the question, what data does this page request?
The story around that is so great.
So if someone, if a database engineer who's trying to debug a performance issue says what,
and they're not really touching Elm at all and they're like, okay, this page is, I know
it's a problem.
You know, where can I get a query so I can test this stuff?
And you have the query as a static file, it's great.
And then they learn, they can just go and get it.
They don't need to, well, I want more people to learn Elm.
It's fine if they don't have to.
So that was a big motivator, just like clarity.
We did have, in some cases with the same visibility approach, one of the primary things to think
about with GraphQL is over querying for data.
So like, I think there's a question of, we kind of mentioned this, but there's deduplication
in your queries, which in Elm GQL is held by fragments, but there's also like reuse
of others in another sense.
And what we found is that you actually, so also with fragments, we have gone about a
year with an older version of Elm GQL, my library, and we've not really had a massive
need for fragments.
We do now, there are a few cases where this shows up, but I think the gut intuition that
we have is that if queries are easy to write and they're quick and you have really wonderful
editor support, you should think about not worrying about sharing stuff.
You should just write a new query because that'll allow two separate instances to diverge
when they need to.
And the opposite of that, when you've overshared between stuff, can be very painful because
you'll be like, this page is very slow.
I don't know why.
It's like, oh, we were selecting this other data that this page technically doesn't really
even care about.
It's the classic don't repeat yourself dilemma between repeating code and repeating knowledge.
And if you deduplicate code that doesn't represent the same knowledge, then those two things
don't want to change together.
And we found that the speed of being able to write these queries changes the math you
need to do.
Because it's like, if you can write a query easily and you can adjust it easily and you
know you're never unsafe, then it doesn't feel like, oh, great, I'm just going to write
a new one.
It's not that big of a deal.
So you shared a few motivations.
Now I'm wondering, you were using Dillon's version before.
That's right.
We still are.
Yeah, you still are.
What pain points did you have that led you to wanting to write a new version?
Yeah, the main pain points we had was questions over sharing of queries.
And we went through a few iterations of performance trying to get some of these pages down to
load reasonably.
And one of the biggest challenges we found was that, oh, there was a shared what's called
the selection set in Dillon's library, which is basically just a selection of fields.
There was a shared selection set that one page was using and probably driving, needed
a lot more data.
But then you have the detail page is selecting a bunch of data.
And then maybe the page that lists a bunch of things is also using that.
And so then you're accidentally over querying a bunch of stuff.
So untangling those, that was challenging.
So you can do separate queries, but you felt pushed towards reusing the same queries.
There's a really interesting discussion around incentives and how those sort of can change
depending on what is easy.
And so, yeah, that's right.
And the other big motivator with the GraphQL thing was the writing experience when you
have a plugin is really nice.
Yeah, there's a lot of good tooling.
Autocomplete is like a superpower.
And so that was really wonderful.
And the last one is we're growing at a sort of a ridiculous pace and wanting to remove
barriers to knowledge.
And we know that GraphQL is sort of our primary domain modeling is the GraphQL layer because
that's how we coordinate all this stuff.
And so making that more accessible on both sides as much as we could was beneficial.
So those cases where if someone needs to drop into a page and understand, oh, we know that
this page takes five seconds to load.
What the heck?
Do they have at their fingertips enough information to debug that in a nice way?
Any comments, Steven, as to these pain points?
You're totally wrong, Matt.
You're totally wrong.
So it's really interesting to hear that story.
And one thing you didn't bring up also, but we've talked about this before, is input objects.
Oh, right.
Yeah, yeah.
I know, like, I mean, from...
Can you share some light on what an input object is?
So I know from my own experience using, as an Elm GraphQL user, it can be really painful
to just...
So input objects can get into these deeply nested data types, these deeply nested key
value pairs.
So basically, for example, if you're sending up...
You've got a user profile page, you're on the page, you select all the stuff for the
user profile, the email address, date of birth, and now you want to update that.
What do you do?
You're not selecting data, you're sending data back.
And so GraphQL has this idea of input objects.
So you're probably going to do not a query, but a mutation GraphQL request.
It's just a semantic difference.
Otherwise, the syntax is the same.
But you're going to be sending data up, which means you're building an input object.
You can also have an input object for specifying filters to filter the data on.
It's just your input data to your GraphQL query.
Which contains a lot of optional things, right?
You're right.
The main irritation that Dillon and I have bonded over is in GraphQL, the default, actually,
you have to mark something as required.
So the default is that something's optional.
But the supremely irritating thing about it is that optional is actually kind of squishy
and weird.
So what optional means is actually there are three states it can be in, not two.
So it's like null times again.
So what it can be is, let's say you have, if you think about it as just like a JavaScript
record, you can have the value either it is present, or it can be where the field is there,
but it's null, or the field is not there.
Now the irritating part about this is that null is generally rarely used.
It is used to say like delete this thing sometimes.
Sometimes people, so it's like one of those things where it's like irritatingly in some
small cases, it's semantically valid.
But usually people mean absent, meaning don't send up this optional thing.
Because in Elm we have to be explicit, we have to sort of represent all three of these.
And I think the confounding factor that Dillon and I have both had to design around is that
if something's optional, you have an optional argument to your query, we want to make sure
that the generated Elm code, if someone changes the schema and adds another optional query,
we don't want your Elm code to break, it should still compile.
So the default of like, I'm going to make a full record that represents all the arguments
and everything's going to be, you know, maybe we represent it not with a like, this like
super maybe, which is like, which Dillon and I have both messed with, which is present
with the data absent or null.
But if you drop that in a full record in Elm, it means your code's not going to compile
if someone adds an optional argument, which people get, they're like, why?
Yeah, and the Elm syntax doesn't have a way to do records with optional fields, except
explicitly having a data type that represents that optionality.
So you can't just leave off a field in your record.
And then Elm says, Oh, well, I'll use this default absent value for the things you leave
off, which is very cumbersome for users, if there are 100 different options, which is
not unheard of in a GraphQL schema, that are possible inputs for an input object you could
These are the possible fields you could provide.
These are the possible filters you could filter on for this table of data.
And now, so there are two different things here.
One is the approach to the generated code for building up input objects in Elm GQL,
which I want to talk about.
You've taken an interesting approach there.
Probably one that might be well suited, like maybe Elm GraphQL, it would be a good fit
for, and I could try that out.
It's not incompatible with the approach.
But the second thing is this query generator approach that Elm GQL uses, you can actually
bypass that where actually you don't need to pass in any of that data.
So in our example of the GraphQL repository that you're selecting all repositories for
the user, you're just passing in a string of the username.
So for the consumer in the Elm code, you need to pass in a string, not an input object.
Building up that input object is handled in the GraphQL query itself.
And therefore, you don't, so you can skip all these levels.
Whereas in Elm GraphQL, my Elm GraphQL library, what you need to do is you need to explicitly
build that up to express a valid query.
So that means since Elm doesn't have a way to do records with like an implicit default
for fields you don't mention, what Elm GraphQL does is the generated code lets you build
these things up with functions where you build up an input object.
It passes you a record with all the defaults set as absent.
And then you can do the record field update syntax where you explicitly set the fields
you don't want to be absent.
And it works, it's type safe, it's perfectly type safe, but you get all of these nested
things where you're passing these functions that give you the default records and it's
very much not ideal.
So that's definitely a real pain point.
And I believe that was one of the motivating cases for you as well.
It was one of the things that we were thinking about for sure is you mentioned filtering.
And filters are usually across the board they are big complicated inputs.
It's like you have like, I want to filter by this and this and this and this and we're
going to add them together and then we're going to add that with an or thing and then
we're going to set this value.
And we had some pretty complicated filtering which emphasized that pain.
And in a vendor Elm GQL, the things that we found that were surprising after we started
messing with it, I mean, one of them that is exactly what you mentioned, right?
So it's like you can just actually build most of the input object in your query, like where
you just have the records there and it's really convenient.
Another few things you can do is that, you know, something can be optional in the schema,
but could can be required for your query.
So an example being like, maybe the query you're like setting requires both if we're
talking to GitHub, requires both the owner and the username, right?
Or the name of the repo, right?
And I think those might be required, but in the schema, let's say they were nullable,
they were optional.
You could actually in Elm GQL, you could actually flag them because you have a little declaration
at the top, here are my variables and what the types are.
And you could actually say like, actually, for this query, these things are required.
And that simplifies the code generation a lot because you basically, this concern doesn't
show up.
So you can just skip it sort of entirely.
Yeah, absolutely.
So, so I think it's really, really cool.
Like I love the motivation of like having shareable GraphQL syntax between different
teams that can speak the same language.
And I think, again, like just sometimes you fire off a mutation request and most of what
you care about is building up this big input object and you don't care that much about
the data or sometimes the data is really, your schema defines it pretty nicely and you
don't need that much fine grain control.
So I think it's great having both of these approaches.
I could certainly imagine myself, like, I mean, I certainly personally, I do really
like having fine grained control over my types.
The types.
Yeah, sure.
Not only through the mechanisms of how I define my GraphQL variables in the query and the
fight starts now.
Like scalers are a great tool for that.
And yeah, the variables.
But I personally do really like having like super fine grained control over that.
I wrote a blog post a while ago called types without borders isn't enough.
That's about my original design of actually maybe a little deja vu here on TypeScript
Oh yeah, there we go.
On TS Interop.
Oh, oh, okay.
So I disambiguated my own tool.
We talked about that in our Elm TS Interop episode.
But in a nutshell, the approach that Elm TypeScript Interop took was you have all of these ports
in your Elm code and it sort of statically analyzes all of that code and it knows all
of the ports you define and all of the types of those ports.
And then it generates TypeScript bindings for that.
So it knows the type information for your Elm application, which is kind of cool.
But then what happens when you want to serialize a custom type?
So in that particular case, there's no way to serialize anything that turns into a custom
type, which is unfortunate.
So that's a hard one.
No way like automatically.
Is that what you mean?
So you could serialize a JSON encode value over to the report, but now it's untyped as
far as TypeScript is concerned.
So I wrote about that journey in this blog post of how I went with a more explicit approach
where you build up the mappings of the types.
It allows you to decouple how you might want to represent something in TypeScript from
how you might want to represent something in Elm, all these classic things that we're
familiar with with this idea of JSON decoders.
So I certainly think both of these approaches have their benefits and their uses.
But I think it's really cool to have both of them out there.
And yeah, really cool to hear about your journey there.
So actually, Matt, you still have both in your project, right?
At Thunder.
That's right.
We're using Dillon's library and we're also using Elm GQL.
I think we're probably like 50 50 right now.
But you're basically intending to use Elm GQL?
We're intending to use Elm GQL.
That's right.
Yeah, that was my question.
Oh, man.
I know we've gone long.
There's a whole other interesting discussion around generating using a schema to generate
mocked values.
This is something that I don't neither library like I wanted this in here or as a concept
because it's something that neither library does right now.
But it would be really fascinating for testing.
I could use an example.
Yeah, totally.
So we had there's a little bit of a story here in that vendor or previously blissfully,
we wanted to use the project Elm program test, which is a way to like basically run, you
know, tests that say like, you know, click this button.
And if you click this button, then this request would be made.
Now, which is cool and valuable, the biggest challenge to using that is data.
For example, for loading a given page, I need to have a certain set of data to actually
just show the page, right?
So we had a version this existed or exists actually in the vendor code base, where it
would look at the query that's being issued by that page to boot it up.
And it would like, look at the schema and say like, okay, I can make you something that
like looks like that.
Even if it's not semantically meaningful at all.
But it's like, here's Oh, a string, great, I'm gonna have a string called placeholder.
And then you'd be able to render the right HTML, this is all done in memory, right?
So this is kind of like, you think about browser automation, but there's no browser being booted
This is a pure piece of JavaScript that's being run again, via Elm program tests.
And so if you can view that page, you can do stuff, you can click buttons, and you can
actually in Elm program test, you can say, well, you know, if this button is clicked,
I want to ensure that this query is fired off.
And one of the and what was interesting, though, is we ran into some challenges with just giving
hard coded scalars back, where it's like, oh, if it's asking for a string, we're gonna
give this string, like one string for all strings, right to rule them all.
So an approach that I'm really interested in exploring, and that Dillon actually mentioned,
suggested, I'm like, oh, that sounds great, is using the schema, or even a query and generating
a set of fuzzers that you could have the query you're asking for.
And then you could craft exactly what, or not exactly, but with the level of definition
you want for what data is returned for a given request.
So what that means of what you could do is, let's say you have a thing where you're keying
something by ID, and you want to make sure like, you know, if I get this data, then the
thing the data that we got is being rendered in the UI, you could actually do that test
because you know, like, okay, I know that the ID is specifically this, you know, and
whatever it is for that page.
And I know that it's being attached as a class or CSS class or something, you can actually
ask Elm program test, hey, is that is that visible?
Anyway, that's, that's the sketched out idea.
Basically generate fuzzers, generate auto mockers or whatever that you could adjust
if you want to.
Like, if you, you could have the auto generate one and if you want something that's more
specific, copy it out, adjust it as you need.
I'm really excited about that idea.
I think it's yeah.
Yeah, the code generation opens up so many, so many cool possibilities.
So one thing I wanted to point out too, as a difference, so there is there is a limitation
of my Elm GraphQL library, which is currently there's no way to use GraphQL variables at
And that is, you know, sometimes people will use GraphQL variables to try to mask certain
sensitive values in their backend logging or...
Oh, interesting.
I didn't know that, but that makes sense.
Caching reasons.
Are those the same as the function parameters again or?
They can be, but essentially, yeah.
But it's a piece of GraphQL syntax that allows you to have a named value, which you can reuse
throughout a query, right?
So in Elm GraphQL, there's no such thing.
You have values, you know, you have input objects, you have selection sets, you have
variables, but there's no place...
The generated GraphQL query that's produced by Elm GraphQL will never include a GraphQL
variable in it.
Because it just drops the values directly in line.
So I'm guessing that's not a problem, but it makes the query bigger.
Is that it?
Well, if you have a particular use case where your backend uses, like, for example, it says,
hey, if you say, get all this data by user ID, and we take in that query, and we cache
it based on this key of the ID that's passed in as a variable.
So certain use cases like that, so that's definitely a limitation, which vendor inc.
Elm GQL does not have that limitation.
So that's definitely one thing to be aware of if you have that use case.
So when you're sorting out variables, there can be some performance things that you can
Like, it's not just caching based on...
Or it can be caching based on the inputs.
But also, if you have a query that is totally...
Is not varying all the time, you can actually cache parsing that query where the backend
can just actually hash that query, and then it doesn't have to parse it and validate it
and everything.
And so that can skip some overhead.
We thought about that, but it's not highest on our list of things we need to resolve performance
But yeah, there is that option.
What is that technique called again, where you can send a hashed version of a query?
You can take it one farther where instead of sending the query, you basically the front
end and the backend know the hash, and they just send up the hash.
Along with some variables, essentially?
And variables.
Yeah, variables are in all of these cases in a different field.
It's really interesting.
It also makes me nervous, but I get it.
It's cool.
One of the interesting things there is you can have an allow list for things that...
Oh, really?
For like hashing behavior or something?
Well, you say like, these are the blessed queries and anything else is...
We just won't run.
We'll just throw an error, right?
So if somebody's poking around trying to do a DDoS attack by finding deeply nested GraphQL
queries or things, you don't have to worry about that.
It's also interesting too, because you could potentially have a backend where you say,
well, I'm going to conform to the shape that I promised to send by this query, but I don't
need to use any of the GraphQL formalities for safely doing the data loader pattern to
make sure I don't have N plus one database queries and stuff.
I can just pretend like it's a rest response, but promise to conform to that shape.
So it's an interesting space that sort of relates to the Elm GQL query generator approach.
So interesting space to explore.
And another thing about Elm GraphQL, so there are certain high level things about the field
aliases are built up for you automatically, for better and for worse.
Like you said in Elm GQL, you get a build time guarantee.
So it makes sure you've done that correctly.
And it is the aliasing thing is interesting.
Just as a small note, you do have a little bit of ability in Elm GQL to influence what
code is generated, what names are used.
Elm GQL specifically emphasizes aliases that you've provided as names that are, it would
prefer to use versus like calculating something from the schema directly.
So if you add an alias, that's a nice way to have some, like know that like that type
for that selected field is going to be named that or that field name in the generated code.
So yeah, which is a thing that like as a concept, cause there's no code generation in Elm GraphQL
as far as queries or stuff.
Yeah, it's just a totally separate thinking.
So one thing I was curious about was, so you mentioned before the recording that like originally
you started out this exploration, not with a query generator style as it is now, but
with a query builder style akin to Elm GraphQL.
So what was that story?
What did that look like?
So originally our big, the big pain point started everything I think was building up
these big input objects, these big filters.
We had this very big input objects and it became very challenging to extend beyond a
certain point.
Was it that bad?
The challenge?
Yeah, it was rough.
Like again, when you're dealing with the, in Elm, you know, you have to deal with the
entire option space explicitly.
So when you have a lot, especially there's this really interesting interaction with optional
If the schema goes, and to be fair, that schema, we, I think know better now as far as how
we create our GraphQL schemas.
And probably if we redid that feature, we wouldn't go quite as nuts as we did, but we
still had to deal with it and sort of compose it.
And so there was a lot of exploration around simplifying input objects initially.
And I think initially also we were kind of wary of the, it had come up, but we're like,
I'm not sure, like, I don't know about the GraphQL to Elm path.
And then I know at a certain point we were just like, no, this seems at the very least
useful in a lot of simpler cases.
And after using it, we're like, oh yeah, no, this actually works in even very complex cases.
We feel pretty good about this.
So that was kind of the progression.
So Elm GQL had a whole version of it that was basically very similar to Dillon's library
with some small changes around input objects.
And then the discovery was we really preferred this GraphQL to Elm approach.
And then partially I was like, okay, does it make sense to have both in one library?
I'm like, I mean, there might be some circumstances where like I thought about you could specify
fragments in sort of like, like in the Dillon way, but partially I'm going to let that need
sort of naturally arise.
I know supporting fragments is like in GraphQL, it's like that makes sense because it's in
the spec.
But so I took out that other part that mirrored Dillon's library and just focused on the GraphQL
to Elm approach.
That's really interesting.
I could certainly imagine a hybrid where you have a query builder style where you could,
for example, like have just like in Elm test files, you have exposed values of type test
and it registers those into the test runner.
You could do something like that where you have these like query builder queries, GraphQL
selection sets that are exposed from a file and those are your fragments.
And then Elm GQL lets you compose those fragments in and then it will use the appropriate types
from those.
So like that was definitely along the lines.
And I think in my previous or strange loop talk where I talk about Elm GQL very briefly,
I mentioned that that was like a thing we're supporting.
Now I'm currently like, well, let's see.
Because I feel as is with just the, without having the defined stuff in Elm, I feel pretty
good about the project.
I'm not ruling it out because ruling out stuff is not always beneficial.
But yeah, it was definitely on my mind of this hybrid approach.
So now that you're using these GQL files, you say that you have auto completion that
is very useful.
Do you also use linters on those kinds of files for instance?
No, but I think there's a, there could be, there's an interesting probably question around
like unused things, but that's an interesting probably discussion around like doing analysis
on the Elm side maybe.
And then figuring out like, it would be interesting to explore.
We haven't run into that need because usually if the unused stuff is the highest value,
just having the query there and where it's like this, I can see it directly and this
is exactly what's being requested.
It becomes pretty easy to audit a query and to remove stuff just manually.
So it's interesting, but not necessarily a high priority is kind of where I would frame
I was mostly wondering like what cool things can you have for the linting on those files?
Cause I have trouble finding out what you could figure out.
I don't know.
I mean, I think some things that are interesting is there's the deprecated directive.
So basically you can know if a field is deprecated, that's interesting information.
It's not really linting though.
It's more to, well, it's applying the schema to your query, right?
So a limited version of linting.
So that surfacing that information is interesting, but yeah, I don't know.
I'd be curious if people can come up with a compelling like benefit or goal in mind,
I'd be curious to hear for sure.
But yeah.
And I guarantee you there's some tooling with like regular GraphQL schemas and GraphQL syntax
queries to look at all of them and find unused fields and things like that.
Like I'm sure it exists.
There is another thing that I think about that, but it's still based around directives
is on our, in our backend schema, though this data isn't public, but when we're compiling
our Elm app, we could make a special version of the schema that had this information.
You could have a directive that informs what permissions are needed to run a query.
That's already in our backend schema.
So basically if there's a query, you annotate it with like this query requires, you know,
and there's an enum that has a bunch of permissions listed.
So like this query to run it, you require these permissions.
I always thought it would be really interesting if there was some way for the front end to
know what permissions were needed.
So that ultimately the idea we would, if someone can't do an operation, we don't want to show
them the UI to do the operation in most cases, but tracking down those permissions thing
is it's a very manual task.
I'm not sure exactly what we do with the information beyond just making it very obvious to the
Elm programmer, but that's sort of a static analysis thing.
It's just, again, it's through that mechanism of directives as opposed to full like analysis.
So I want to make sure we bring up this tool by Harm Boschlo.
I hope I'm pronouncing that okay.
Called GraphQL to Elm.
So this is a...
Probably a better name than mine.
It's very obvious what it does.
So it's a similar query generator tool akin to Elm GQL.
So I wanted to just make sure we bring that up, that it's a similar approach.
Yeah, I haven't.
I only...
I think it was on my radar a little bit, but I've never played with it.
I think it seems pretty similar, but I personally don't know the ins and outs of the differences
of it, but I'd be curious if someone compared the two.
That'd be great to hear a tweet at us or mention it in the Elm Radio Slack channel or...
Let us know if you've played around with it and take a look at Elm GQL.
Let us know what the differences are there.
I'm guessing there are probably some subtle differences between the way that you manage
input objects.
So like in Elm GQL, we mentioned that you can sort of get around needing to build up
deeply nested input objects by the way you pass in variables and do most of that in GraphQL
syntax, not Elm syntax.
So if you... for that GraphQL getting all repositories example, if you have your variable
be this input object that lets you define all of the options instead of saying the input,
the GraphQL variable it's going to take in is a string for the username you want to list
all the repositories for.
If instead you say it's just the input objects that lets you define all of the custom filters
that the GraphQL API takes.
Then now you get to use the Elm GQL generated code that lets you build up these input objects.
And I think that's like one of the really interesting bits of design work there that...
So I'd be interested to compare those two.
I'm also really interested to compare that with Elm GraphQL because I think there's a
lot I could learn from that approach there.
Yeah, it's interesting.
I'm curious to see how the two tools kind of square up or the three tools rather.
Reenter, only one can leave.
So I feel like Matt, you made a pretty compelling case for your tool.
Got anything else you would like to add?
I mean, I think...
I mean, are you yourself convinced by Matt's approach or are you going to continue using
your own tool?
Well I think it's really interesting the story of like what do you want the source of truth
to be.
And like one of my main motivations...
Actually at the time that I designed my Elm GraphQL library, there was an Elm GraphQL.
It was actually...
If you did npm install elmgraphql, this was the tool.
Like Jay Haasen or...
I'm sorry, I'm botching the username, but something along those lines.
I'll drop a link to this deprecated...
Now deprecated repo, but it doesn't work for beyond Elm 18, I believe.
But that was using the query generator approach.
And I was aware of such approaches when I built Elm GraphQL.
And I built Elm GraphQL because I thought I would love to just work in Elm.
And I wanted to express these things at a high level with Elm.
I wanted IDE completions in Elm.
I wanted to have fine grained control over the types I build up in Elm and how I map
So I do personally love having fine grained control over how I build up my types.
Even if I have control over my GraphQL schema, I really like that.
I do like being able to build up a selection set within an opaque type and expose that
so that it's not a type alias that could be constructed anywhere.
It is an opaque type where that knowledge is constrained to live in that one place.
And the only way you can get that type is through that selection set.
I think that's...
So the way I like to maintain my code, I really like maintaining it in Elm and being able
to use...
Elm tooling for it, yeah.
That I can craft myself.
So that's just my preferred workflow.
With that said, I think it's...
I mean, even though that's my preferred approach, it definitely gets my interest for things
I don't know, maybe I just want to fire off a bunch of data to the server and I don't...
The data I'm getting back is not all that nuanced or interesting or important to my
domain modeling.
So yeah, I think it's really cool to have different approaches out there, but I think
they both have their uses.
Yeah, I would just recommend for people trying it out, probably if you're evaluating tools
to give them a go.
Like see if you can't take a day if you can.
I mean, choosing how you interact with data is probably one of the most foundational decisions
you're going to make on the client.
So play with it, see what you vibe with.
I think the important part...
The first important part is both of these are type safe ways of interacting with an
That's wonderful.
And yeah.
Yeah, and there's a lot of really cool tooling out there.
I can't remember the name of the tool, but a lot of these graphical, like GraphQL...
The explorers for a lot of stuff.
I love having an API explorer like that.
It's really cool.
A lot of them these days have little check boxes where you can just say, like, oh yeah,
I want this data, I want this data, I want this data.
And it builds up your GraphQL query for you.
Once again, it's like static types, man.
When you have that kind of stuff, you can unlock a lot of those sorts of experiences,
which are nice.
My vision is like, I would love it if people were like, oh yeah, have you tried Elm?
It's amazing.
It has this like, you can just click this UI where you just click this thing and it
builds up all these different types with these little check boxes.
I want people to be saying that about using Elm APIs because all the information is there,
but we've got more people fiddling with tools in the GraphQL tooling space.
We need more people doing that in Elm.
You want to be a checkbox developer.
I mean, if you know that there's a set of finite possibilities, why not present them
in a finite way to me?
Yeah, man.
I'm right there.
It was a pleasure having you on to hash this out and to hear your story there.
And yeah, thanks so much for coming back on, Matt.
This is great.
It's always exciting.
Are there any, if people have questions, where should they go?
And do you have any resources to share on this topic if they want to learn more?
So the Elm GQL repo will have a guide.
I am currently working on a GitHub example repo to show how it asks for queries and stuff.
It might be kind of interesting actually to have a shadowing repo for Dillon's library.
Shadowing meaning if we had the same stuff so people could be like, oh, that's what that
looks like.
This is what this looks like.
I'm not saying I'm not putting work on Dillon's plate or my plate, but I'm making an example
So look for slash Elm GQL and there should be a number of resources to read
more about it or play with it.
Just to clarify, Elm dash GQL.
Elm dash GQL.
Thank you so much.
And you're in.
Until next time.
Until next time.