Gelly Fragments

There's often a need to re-use specific parts of Gelly queries in different places, in the same way you might re-use a JavaScript function as part of a larger piece of code, or re-use part of an HTML template in different outer contexts. Gelly supports fragments to facilitate composition and re-use among queries. The Gadget platform also uses fragments to allow Gadget developers to co-operate with the automatic schema generation and extend it. Fragments in Gelly work similarly to GraphQL, but don't have an exact analog in SQL.

Fragments can only be declared at the top level, and are declared using the fragment keyword. They are defined with a name that is then used to refer to them later, followed by the type of the object they are selecting fields from. Fragments can contain field selections, nested field selections, and relational commands. For example, we could use a fragment to specify which fields want to select from the posts model. We use the fragment keyword to start a fragment definition, give it a name, and then use the same field selection syntax you might use in a query.

gelly
1fragment PostDetails on Post {
2 id
3 title
4}
5
6query {
7 posts {
8 ...PostDetails
9 [where published]
10 }
11}

Queries can apply fragments using the spread operator, which is the .... Spreading a fragment can be thought of as copy-pasting the fragment's source code into the location of the spread.

In order to support re-usability of relational commands too, fragments support embedded relational commands. When spread, these fragments apply those commands to the query, merging the query's commands with their own. For example, we could have a PublishedPostDetails fragment that we re-use in several queries that selects some fields as well as filters the posts that are returned. Queries that spread fragments can add more filters, or add other relational commands, and still select other fields.

Fragments with relational commands
gelly
1fragment PublishedPostDetails on Post {
2 id
3 title
4 [where published]
5}
6query {
7 posts {
8 ...PostDetails
9 author {
10 id
11 }
12 [limit 10]
13 }
14}

Gelly doesn't allow both a fragment and a query that spreads it to both specify a group by relational command or an order by relational command. Only the fragment or the query can use these commands, but not both.

Fragment variables

Gelly fragments can accept arguments and then be spread with different values more than once. Variables can be used within the fragment to change behavior, and are really useful for re-using a particular fragment more than once with slightly different specifics in each place. Fragment variables are also used for configuring Gadget's permissions system with custom logic that can change behavior based on the $currentUser variable.

Fragment variables are defined the same way as query variables using parentheses after the name of the fragment definition. Fragments can then be spread with different values for these variables in any context where a fragment would normally be valid.

Fragments with variables
gelly
1fragment PostsForAuthor($authorId: ID!) on Post {
2 id
3 title
4 [where authorId = $authorId]
5}
6query {
7 posts {
8 ...PostsForAuthor(authorId: "123")
9 author {
10 id
11 }
12 [limit 10]
13 }
14}

Fragments can take many variables and can use the variables within field selections or relational commands without issue. Fragment variables can also be passed values from outer query variables or arbitrary expressions.

Fragments with variables
gelly
1fragment PostDetails($onlyInteresting: Boolean) on Post {
2 hotRightNow: $onlyInteresting ? score > 100 : false
3 [where $onlyInteresting ? score > 10 : true]
4}
5query($full: Boolean) {
6 posts {
7 id
8 title
9 ...PostDetails(onlyInteresting: $full)
10 [limit 10]
11 }
12}

Fragment variables in Gelly don't work the same way as variables work in GraphQL. Variables in Gelly are similar to arguments in JavaScript or Python where they must be declared at the top of a fragment or query, and only the variables that are declared are accessible for the duration of that fragment or query. If you want to pass a value from a query stanza into a fragment stanza, you must declare that the fragment accepts that variable, and pass it in when you spread the fragment.

Fragments with variables
gelly
1# the $onlyInteresting variable won't be available in this fragment unless we declare it at the top of the fragment
2fragment PostDetails($onlyInteresting: Boolean) on Post {
3 hotRightNow: $onlyInteresting ? score > 100 : false
4 [where $onlyInteresting ? score > 10 : true]
5}
6query($onlyInteresting: Boolean) {
7 posts {
8 id
9 title
10 # the $onlyInteresting variable must be passed into the fragment, even if it has the same name in the query context
11 ...PostDetails(onlyInteresting: $onlyInteresting)
12 [limit 10]
13 }
14}

Field fragments

Gelly has a second fragment syntax for fragments that define one new field on a model. These are called field fragments, and are a Gelly-only concept that doesn't really have an analog in GraphQL or SQL. Field fragments are fragments which return a single value, so they are different than normal fragments because they can't select multiple fields from the input. Field fragments are used when implementing a computed field in Gadget. You can read more about computed in the Gelly Guide.

Field fragments are specified using the field keyword, followed by the name of the field being defined, followed by the type of the model the field is being defined on. Inside the curly braces Gelly expects to find exactly one expression. For example, we could define a field on posts called isPopular with a field fragment.

isPopular field fragment
gelly
1# in the Gadget editor, in isPopular.gelly
2field isPopular on Post {
3 score > 100
4}
5# in a Gelly query later
6query {
7 posts {
8 title
9 isPopular
10 [limit 10]
11 }
12}

Field fragments can specify arbitrary expressions, including useful stuff like aggregates on relationships of the model being queries. For example, we could use a field fragment on a computed to power a new field of the Author model which counts the number of popular posts that author has ever written.

popularPostCount field fragment
gelly
1# in the Gadget editor, in popularPostCount.gelly
2field popularPostCount on Author {
3 count(posts, where: posts.score > 10)
4}
5# in a Gelly query later
6query {
7 authors {
8 id
9 name
10 popularPostCount
11 [limit 10]
12 }
13}

Field fragments aren't able to use relational commands.