Computing in Gadget with Gelly
Gelly is the data access language powering advanced features in Gadget like Filtered Model Permissions. Gelly is a superset of GraphQL that follows the same principles of declarative queries returning predictable results.
Filtered model permissions look like this:
fragment PostFilter($session: Session) on Post {*[where !isSpam || authorId == $session.userId]}
Gelly snippets look like this:
gelly1fragment Details on Product {2 # select the name field3 name45 # select a field named inStock, which is true if the6 # inventory count is greater than 07 inStock: inventoryCount > 089 # sort the returned list of Product by the id field descending10 [order by id desc]1112 # return some fields the first three variants of each product,13 # sorted by most recently created14 variants {15 id16 name17 price18 [limit 3 order by createdDate desc]19 }2021 # and return an aggregate count of the images for the product22 count(images.id)23}
Why a different language?
Gelly is used to efficiently perform computations at scale in Gadget applications.
JavaScript works well for adding functionality to Gadget applications when implementing behavior, but some applications have more complex computational needs.
Computations in Gelly are declarative such that Gadget can pre-compute or re-compute the values of Gelly snippets very efficiently across a high number of rows. Currently, Gadget compiles Gelly into high performance SQL to execute inside the database with a custom caching layer on top, which makes Gelly fast while remaining consistent. Gadget does this in order to power features like conditional permissions that decide if a row is visible using data from related rows. Neither of these is possible at scale if the computation is defined in JavaScript because Gadget would have to run the computation for every row in the database to know which ones to return.
Gelly for SQL lovers
Gelly is similar to, and inspired by, SQL! A Gelly query is also an upfront, declarative ask for some data based on relational algebra, but it's different in that it allows relationship traversal, fragments, and more ergonomic expressions. Gelly eliminates a few annoyances from plain old SQL such as having to manually specify join conditions all the time, or having to execute multiple queries or complicated json_agg
functions to retrieve related data, and provides quality-of-life improvements such as not requiring trailing commas or a specific statement order.
Gelly for GraphQL lovers
Gelly is also similar to GraphQL and extends it with the ability to do a bunch of fancy stuff you can't do with normal GraphQL APIs. Gelly snippets can contain arbitrary computations that build new expressions with normal-looking code, as well as universally available commands to filter, sort, or aggregate a list of data.
GraphQL differences
Even though Gelly and GraphQL are similar, there are some key differences between the two systems:
- Gelly queries can contain arbitrary expressions which are evaluated on the server side
This is so the frontend can push as much work to the server as possible, and so that they don't need to manually add new, bespoke API endpoints for each separate thing they want to do. Sometimes it is simpler to just ask the server to upcase a string, filter on an expression, or group by an arbitrary field that the user has selected. So, Gelly has math, function calls, aggregations, and relational commands built in.
- Gelly has relational commands available on every relation instead of some GraphQL fields having arguments
In Gelly, the relational commands [where]
, [order by]
, [limit]
and [group by]
are universally available on every relation. This allows developers to work quickly and leverage the automatic query compilation and execution that Gadget does for each Gadget application. Some GraphQL APIs expose similar facilities to Gelly's relational commands, but as bespoke field arguments that often require backend work to set up and make performant. Aggregations in Gelly aren't exposed as other strange fields in a schema and are instead available as aggregate functions
- Gelly doesn't have Relay style Connections, and instead has a formal idea of a relation
Gelly is a syntax around a fundamentally very powerful concept called relational algebra. Just like in the algebra, relations in Gelly are things you can manipulate, and so we don't work with them as just more types in the system, and instead bake the idea in deeply. There are no edges { node { ... }}
selections in Gelly, there are just subselections of relations. We have big plans for enhancing Gelly with support for the full relational algebra including joins as well as a relational chaining operator for implementing subqueries.
- Gelly doesn't support Mutations
GraphQL supports writes and Gelly does not (yet). If you're building a Gadget app, you can use the GraphQL API to execute writes to your application.
- Gelly doesn't support Subscriptions or
@live
queries
Some GraphQL servers support subscribing to a specific field, and some even support asking for arbitrary changes to a query result over time, both in a push style. Gelly doesn't yet support these facilities.
- Gelly
null
works like SQLNULL
, not like JavaScript'snull
, wherenull == null
givesnull
.
Gelly is powered by SQL underneath the hood, and so inherits SQL's null semantics. This means that null == null
returns null
in Gelly, not true
. To check if something is null, you should use the isNull
function.