Native GraphQL, GraphQL as a Database Query Language


GraphQL was originally not designed as a database language yet it's quickly becoming a popular choice to interact with databases. What if databases natively support GraphQL default? What are the advantages of native GraphQL support? How is a GraphQL query handled behind the scenes in FaunaDB and what database guarantees does such an approach provide?


# graphql as a database query language

Hi everyone, I'm Bricht, I'm super excited today to talk at the graphql Galaxy conference. I'm DataBricht on Twitter, I work for the FANA database and today I'm going to talk about Native graphql or graphql as a database query language.

Now if we talk about Native graphql at FANA, what does it mean? Well, first of all, we have a FANA query language which we call FQL and basically Native graphql means that a graphql query is going to translate into one FQL query. That one-to-one translation has huge advantages. So first of all, you might wonder which advantages we'll look into that. And question two, why doesn't everyone do this if there are such advantages? To answer these questions, we actually have to answer other questions like how do graphql resolvers work?

So let's take a detour. How do graphql resolvers work? Well typically if you have a query like this with getList, todo, title, every field in here like getList and todo and title are fields will map on a function. So getList will be a function and that will delegate to the todo function that will delegate again to the title function, for example, to the title attribute. This is a resolver chain, which is a chain of functions, but it's actually more of a resolver tree of function calls because here there's one function that calls n functions. And if we turn this around, we get n plus one and it's basically a problem. And this is actually called the n plus one problem. That's why I turned it around. And when is this a problem? Well, basically if you're going to call the database for each of these resolvers, because then you get n plus one database calls, which is not efficient. So question four, how can we solve the n plus one problem? Well there are multiple solutions. Solution one is batching or n in-memory caching. So in that approach, we're going to hook into these functions, for example, to do.titles and just wait until all the todo.titles are called and then combine these. So instead of going to do n calls for these todo.titles, we're going to do one call. So in total, two calls. That's batching. And that's often combined with caching. So if a similar call comes in, then instead of going to the database, we can go to an in-memory cache. So we don't hit the database at all. A very popular implementation is Facebook's data loader, which you can just plug in on top of your resolvers and it will handle it for you. However, there's a problem with this solution as well. It should in fact be a last resort.

## Related [graphql as a database query language]( - Talk by DataBricht from the graphql Galaxy 2019 conference

Well, why? Your data is no longer live. It's no longer consistent. You can't apply it on everything. You can't batch everything. So you will have still multiple calls. What about caching validation, memory pressure that you have to deal with suddenly? So it introduces complexity. So the first question, which advantages that FANAS approach provides? Well, it doesn't deal with these problems because it doesn't have these problems. It is live by default. It is consistent. It requires no extra work and there is no memory constraint problem. It just works out of the box. So you don't have to do this. Solution two, generate one query, which is what FANAS does behind the scenes. But why doesn't everyone do that? If we would look at SQL, for example, and let's say we would select a star from lists where ID is equal to something, then we would go to the to-do calls and do the same and try to concatenate that query. Of course, we'll have to do it for multiple to-dos. So we'll end up with a join. And the problem is if we go deeper like that in a graphql traversal, we might end up with a lot of joins. Now, not only is this super complex to analyze this query and then generate SQL from it and then transform the results back to a graphql format, it might also be inefficient depending on the joins. You might overfetch a lot and then have to throw away things. And then how are we going to paginate this? ID 100 might not be exactly what you're looking for. So the problem here is that what joins solve, which is a join between two tables, is a different problem than what the actual problem is, which is more like a tree traversal problem or a graph-like problem. So joins are maybe the wrong tool for the job. So there is an implementation, a very impressive implementation called JoinMonster, which actually comes from the problem they're trying to solve, a monster join that might be the result of a graphql query. If you look at the work involved, you can see that it's a complex problem to solve. That's question four.

How can we solve the n plus one problem? So the two solutions. That brings us back to question two. Why doesn't everyone do this? Well, we just showed it. The query language might not fit the problem or the execution plan might not fit the problem. Then of course, why does SQL does fit the problem? Well, we do it quite differently because it's a different query language and has quite graph-like properties. So if we would look at the same query, we would start by getting a list with match index and then the list ID. We would immediately wrap it in paginate. So we actually will have pagination on every level and very sane pagination with an after and before cursor that is always correct. Then we just map over the results of these lists and we would call a function. That's actually just like a normal programming language where you would just map over something and then call the function. In that function, we can do whatever we want. And if we look at the get to dos there, well, what is this? There's just a javascript function because I'm using the javascript driver for FQL where we just throw out more FQL, pure function composition. Then we see the same pattern, paginate and map. So we have the second level of pagination immediately and map and again, a function that will be called. This is actually a graph-like reversal that we're just implementing in FQL because that's possible it was super easy for Fauna to implement that one-to-one translation from graphql to FQL. So what is actually happening here, if we see and look at the query execution is that we map get over all the lists, then we paginate that immediately. And then we just continue map getting and paginate on every level. There is no monster join problem because we do it completely different. So we don't have to solve that problem. So question five, that's why FQL actually fits the problem. Back to question one, which advantages does that bring? Because we have mentioned the advantages, but there are others because we have the same advantages as the rest of the normal native FQL language. We can actually combine that with FQL and use FQL for the flexibility and power and graphql for the ease of use. We have multi-region out of the box, scalability out of the box. We have a hundred percent as it and transactionality about out of the box. So that's what native graphql is.

I hope you like that idea.

And if you want to try it out for free at

8 min
02 Jul, 2021

Check out more articles and videos

We constantly think of articles and videos that might spark Git people interest / skill us up or help building a stellar career

Workshops on related topic