These docs are for a different version of Gadget than what the example-app app is using.

Switch to the correct version

Sorting 

The example-app GraphQL API can sort the records returned for each read by the available fields for each model. If you pass the sort argument, you can control which fields the response is sorted by and in what direction.

Sort on a publishedAt Date/Time field for a Post model
await api.post.findMany({
sort: {
publishedAt: "Descending",
},
});
1const [result, refresh] = useFindMany(api.post, {
2 sort: {
3 publishedAt: "Descending",
4 },
5});
6const { data, error, fetching } = result;
1query FindManyPosts($sort: [PostSort!]) {
2 posts(sort: $sort) {
3 edges {
4 node {
5 id
6 publishedAt
7 }
8 }
9 }
10}
Variables
json
{ "sort": { "publishedAt": "Descending" } }

You can sort on findMany, findFirst, and maybeFindFirst read calls using the API Client in Gadget.

Sort order 

Records can be ordered in either Descending or Ascending order for each field they are sorted on. Descending order will return the highest values first, while Ascending order will return the lowest values first. If sorting by multiple fields, each field can have a different order.

Sorting by multiple fields 

Records can be sorted by multiple fields by passing multiple sort orders in an array to your API call, like [ { fieldA: "Descending" }, { fieldB: "Ascending" }]. The first field will be used to order all the records, and then the second field will be used to break ties where records have the same value of the first field. Subsequent sorts will be used to break ties in the second field, etc.

Sort on category and then publishedAt for a Post model
await api.post.findMany({
sort: [{ category: "Ascending" }, { publishedAt: "Descending" }],
});
const [result, refresh] = useFindMany(api.post, {
sort: [{ category: "Ascending" }, { publishedAt: "Descending" }],
});
const { data, error, fetching } = result;
1query FindManyPosts($sort: [PostSort!]) {
2 posts(filter: $filter) {
3 edges {
4 node {
5 id
6 publishedAt
7 }
8 }
9 }
10}
Variables
json
{ "sort": [{ "category": "Ascending" }, { "publishedAt": "Descending" }] }

Available fields for sorting 

Records can be sorted by most fields of their model in either the Ascending or Descending direction. The following field types are sortable:

Field typesNotes
string, email, urlSorted using PostgreSQL's alphanumeric sorting rules
rich textSorted using PostgreSQL's alphanumeric sorting rules on the markdown source text
numberSorted by the number's value along the number line
booleanSorted with true higher than false. Descending puts trues first
date / timeSorted by the date and time's value, with earlier dates coming before later dates when using Ascending order
idSorted by the ID's numeric value, with lower IDs coming before higher IDs when using Ascending order
enumSorted by the enum values as strings, using PostgreSQL's alphanumeric sorting rules
jsonSorted by the JSON string representation, using PostgreSQL's alphanumeric sorting rules
vectorSorted by a given vector distance operation

Alphanumeric sorting rules 

Gadget uses PostgreSQL's alphanumeric string sorting rules under the hood, which sorts spaces first, then numbers, then upper case letters, then special symbols, then lower case letters, then nulls.

For example, the following list of strings sorts in this order when sorted Ascending:

1" spaces"
2"10-4"
3"123"
4"Apple"
5"Cube"
6"___"
7"anjou pear"
8"banana"
9null

Sorting by vector distance 

vector fields support vector-specific sort types for returning records in order of distance to a given vector. This implements a nearest neighbor search by querying all your stored vectors to find those that are most similar to an input vector on demand.

To sort by vector distance to a given vector field, pass an object with a to: input field with the vector to sort by.

If we have a document model with an embedding field of type vector, we can sort by the cosine distance to a given vector like so:

Sort on cosine similarity to a given vector
1await api.document.findMany({
2 sort: {
3 embedding: {
4 cosineSimilarityTo: [1, 2, 3], // etc
5 },
6 },
7});
1const [result, refresh] = useFindMany(api.document, {
2 sort: {
3 embedding: {
4 cosineSimilarityTo: [1, 2, 3], // etc
5 },
6 },
7});
8const { data, error, fetching } = result;
1query FindManyDocuments($sort: [DocumentSort!]) {
2 posts(filter: $filter) {
3 edges {
4 node {
5 id
6 embedding
7 }
8 }
9 }
10}
Variables
json
{ "sort": { "embedding": { "cosineSimilarityTo": [1, 2, 3] } } }

Available vector distance operations 

Vectors can be sorted by cosine similarity and L2 (Euclidian) distance to a given vector. Gadget generally recommends using cosine similarity for sorting, as it is more robust to changes in vector magnitude.

  • To sort by cosine similarity, pass a sort like { fieldName: { cosineSimilarity: { to: [1,2,3] }}}. Cosine similarity will order by the most similar vectors first, ie cosine similarity descending.
JavaScript
1await api.document.findMany({
2 sort: {
3 embedding: {
4 cosineSimilarityTo: [1, 2, 3],
5 },
6 },
7});
  • To sort by L2 distance, pass a sort like { fieldName: { l2Distance: { to: [1,2,3] }}}. L2 distance will order by the closest vectors first, ie L2 distance ascending.
JavaScript
1await api.document.findMany({
2 sort: {
3 embedding: {
4 l2DistanceTo: [1, 2, 3],
5 },
6 },
7});

Sorting by least similar 

Vectors can be sorted for least similarity by using one of the vector distance sorts with the order: "Ascending" option. This will return the least similar vectors first.

Sort on cosine similarity to a given vector with least similar first
1await api.document.findMany({
2 sort: {
3 embedding: {
4 cosineSimilarityTo: [1, 2, 3], // etc
5 order: "Ascending",
6 },
7 },
8});
1const [result, refresh] = useFindMany(api.document, {
2 sort: {
3 embedding: {
4 cosineSimilarityTo: [1, 2, 3], // etc
5 order: "Ascending",
6 },
7 },
8});
9const { data, error, fetching } = result;
1query FindManyDocuments($sort: [DocumentSort!]) {
2 posts(filter: $filter) {
3 edges {
4 node {
5 id
6 embedding
7 }
8 }
9 }
10}
Variables
json
{
"sort": { "embedding": { "cosineSimilarityTo": [1, 2, 3], "order": "Ascending" } }
}

Unsortable fields 

Currently, Gadget does not support sorting records by a field of the following types:

  • file
  • encrypted string
  • role list
  • password
  • has many
  • has one
  • has many through

Filtering 

The example-app GraphQL API can filter records when reading to only the records matching certain conditions. If you pass a filter argument to a read API call, only records that match the filter will be returned.

Filter on a boolean isPublished field for a Post model
await api.post.findMany({
filter: {
isPublished: { equals: true },
},
});
1const [result, refresh] = useFindMany(api.post, {
2 filter: {
3 isPublished: {
4 equals: true,
5 },
6 },
7});
8const { data, error, fetching } = result;
1query FindManyPosts($filter: [PostFilter!]) {
2 posts(filter: $filter) {
3 edges {
4 node {
5 id
6 isPublished
7 }
8 }
9 }
10}
Variables
json
{ "filter": { "isPublished": { "equals": true } } }

You can filter on findMany, findFirst, and maybeFindFirst read requests using the API Client in Gadget.

Available fields for filtering 

Records can be filtered by most fields of their model, and the type of each model field determines what kind of filters are available in the API. For each field, Gadget uses a specific type for filtering that field type:

Field typesFilter GraphQL Type
string, rich text, email, urlStringFilter
numberFloatFilter and IntFilter
booleanBooleanFilter
date / timeDateTimeFilter
idIDFilter
enumSingleEnumFilter and MultiEnumFilter
jsonJSONFilter
vectorVectorFilter
record stateStateFilter
belongs toIDFilter

Each model then has a filter-specific GraphQL type that details the types of filters available on the selected model. For example, if there is a Post model to capture blog post records, there will also be a generated PostFilter GraphQL type. This filter type can be viewed in the API Reference or API Playground.

Want to see your app's filter typing?

Examine the filter typing of your app's API by checking the documentation in the API Playground. All filter types will be visible for all fields on your app's models.

Combining filters 

Records can also be filtered by multiple different fields. If you want to combine filters using boolean logic, nest them under the AND, OR, or NOT keys of a parent filter.

Combine filters - filter for published posts with over 500 words
await api.post.findMany({
filter: {
OR: [{ isPublished: { equals: true } }, { wordCount: { greaterThan: 500 } }],
},
});
1const [result, refresh] = useFindMany(api.post, {
2 filter: {
3 OR: [
4 {
5 isPublished: { equals: true },
6 },
7 {
8 wordCount: { greaterThan: 500 },
9 },
10 ],
11 },
12});
13const { data, error, fetching } = result;
1query FindManyPosts($filter: [PostFilter!]) {
2 posts(filter: $filter) {
3 edges {
4 node {
5 id
6 isPublished
7 }
8 }
9 }
10}
Variables
json
1{
2 "filter": {
3 "OR": [
4 { "isPublished": { "equals": true } },
5 { "wordCount": { "greaterThan": 500 } }
6 ]
7 }
8}

Filters can be nested deeply by passing multiple levels of boolean condition filters.

For example, we can fetch orders from a Shopify Order model where the total is between $100 and $200, and the order is either paid or refunded:

Combine filters - filter for orders matching many conditions
1api.shopifyOrder.findMany({
2 filter: {
3 AND: [
4 {
5 AND: [
6 { totalPrice: { greaterThan: 100 } },
7 { totalPrice: { lessThan: 200 } },
8 ],
9 },
10 {
11 OR: [
12 { financialStatus: { equals: "paid" } },
13 { financialStatus: { equals: "refunded" } },
14 ],
15 },
16 ],
17 },
18});
1const [{ data, error, fetching }, refresh] = useFindMany(api.shopifyOrder, {
2 filter: {
3 AND: [
4 {
5 AND: [
6 { totalPrice: { greaterThan: 100 } },
7 { totalPrice: { lessThan: 200 } },
8 ],
9 },
10 {
11 OR: [
12 { financialStatus: { equals: "paid" } },
13 { financialStatus: { equals: "refunded" } },
14 ],
15 },
16 ],
17 },
18});
1query FindManyShopifyOrders($filter: [ShopifyOrderFilter!]) {
2 shopifyOrders(filter: $filter) {
3 edges {
4 node {
5 id
6 totalPrice
7 financialStatus
8 }
9 }
10 }
11}
Variables
json
1{
2 "filter": [
3 {
4 "AND": [
5 { "totalPrice": { "greaterThan": 100 } },
6 { "totalPrice": { "lessThan": 200 } }
7 ]
8 },
9 {
10 "OR": [
11 { "financialStatus": { "equals": "paid" } },
12 { "financialStatus": { "equals": "refunded" } }
13 ]
14 }
15 ]
16}

Filter types 

There are many different filter types available in Gadget. Each filter type corresponds to one or more field types.

StringFilter 

The StringFilter type provides a filter definition for the following field types: string, rich text, email, url

The StringFilter type has the following fields:

StringFilter fields
1equals: String
2notEquals: String
3isSet: Boolean
4in: [String]
5notIn: [String]
6lessThan: String
7lessThanOrEqual: String
8greaterThan: String
9greaterThanOrEqual: String
10startsWith: String

An example of a StringFilter being used to filter students by name:

JavaScript
1// get all students with the firstName "Taylor"
2await api.student.findMany({
3 filter: {
4 firstName: {
5 equals: "Taylor",
6 },
7 },
8});

The startsWith filter field is unique to StringFilters, and checks to see if a string begins with the provided value. For example:

JavaScript
1// get all blog posts that have titles starting with "How to"
2await api.post.findMany({
3 filter: {
4 title: {
5 startsWith: "How to",
6 },
7 },
8});

FloatFilter and IntFilter 

The FloatFilter and IntFilter types provide filtering for the number field type. The FloatFilter type is used for fields that have a decimal value, and the IntFilter type is used for fields that have an integer value.

Note: The Float scalar type represents signed double-precision fractional values as specified by IEEE 754.

Both the FloatFilter and IntFilter types have the following fields:

FloatFilter and IntFilter fields
1equals: Float
2notEquals: Float
3isSet: Boolean
4in: [Float]
5notIn: [Float]
6lessThan: Float
7lessThanOrEqual: Float
8greaterThan: Float
9greaterThanOrEqual: Float

An example of a FloatFilter being used to filter blog posts by word count:

JavaScript
1// get all blog posts that are over 500 words
2await api.posts.findMany({
3 filter: {
4 wordCount: {
5 greaterThan: 500,
6 },
7 },
8});

BooleanFilter 

The BooleanFilter type provides filtering for the boolean field type. You can filter to records that have the boolean set to true, false, or filter to records that have a value set at all.

BooleanFilter fields
equals: Float
notEquals: Float
isSet: Boolean

An example of a BooleanFilter being used to filter blog posts by isPublished = true:

JavaScript
1// get all blog posts that are published
2await api.posts.findMany({
3 filter: {
4 isPublished: {
5 equals: true,
6 },
7 },
8});

An example of a BooleanFilter being used to filter blog posts by if the isPublished field is set at all, to either true or false:

JavaScript
1// get all blog posts that have a value for the `isPublished` boolean (either true or false)
2await api.posts.findMany({
3 filter: {
4 isPublished: {
5 isSet: true,
6 },
7 },
8});
9
10// get all blog posts that have `isPublished` set to null
11await api.posts.findMany({
12 filter: {
13 isPublished: {
14 isSet: false,
15 },
16 },
17});

DateTimeFilter 

The DateTimeFilter type provides a filter definition for the date / time field type

DateTimeFilters need to have values provided as a date-time string in UTC, for example, 2007-12-03T10:15:30Z. To get this value from a JavaScript Date object, you can call .toUTCString() on your Date object: new Date().toUTCString();

DateTime objects are only stored to the nearest millisecond if you are filtering with smaller increments of time such as nanoseconds, the value will be truncated to only include milliseconds.

The DateTimeFilter type has the following fields:

DateTimeFilter fields
1equals: DateTime
2notEquals: DateTime
3isSet: Boolean
4in: [DateTime]
5notIn: [DateTime]
6lessThan: DateTime
7lessThanOrEqual: DateTime
8greaterThan: DateTime
9greaterThanOrEqual: DateTime
10before: DateTime
11after: DateTime

The before field is the same as the lessThan field, and the after field is the same as the greaterThan field. The before and after options have been added for better readability and understanding when talking about comparing date / time fields.

An example of a DateTimeFilter being used to get all blog posts written in the past week:

JavaScript
1// get all blog posts written last week
2const oneWeekAgo = new Date(Date.now() - 604800000);
3await api.post.findMany({
4 filter: {
5 createdAt: {
6 after: oneWeekAgo,
7 },
8 },
9});

IDFilter 

The IDFilter type provides a filter definition for the id and the belongs to field type.

The IDFilter type has the following fields:

IDFilter fields
1equals: GadgetID
2notEquals: GadgetID
3isSet: Boolean
4in: [GadgetID]
5notIn: [GadgetID]
6lessThan: GadgetID
7lessThanOrEqual: GadgetID
8greaterThan: GadgetID
9greaterThanOrEqual: GadgetID

An example of an IDFilter being used to filter by a list of students:

JavaScript
1// get all students with ids that match 1, 2, or 3
2await api.student.findMany({
3 filter: {
4 id: {
5 in: [1, 2, 3],
6 },
7 },
8});
Filtering on belongs to relationships 

You can filter one model by its belongs to relationship to another model using the other model's ID. For example, if we have a comment model that belongs to a parent post model, we can fetch all the comments for a given post using a filter on the comment model's post field:

JavaScript
1// get all comments belonging to the post with id 42
2const commentsForPost = await api.comment.findMany({
3 filter: {
4 post: {
5 equals: 42,
6 },
7 },
8});

Filtering on belongs to relationships supports the other filtering operators as well, so you can also filter out nulls with isSet, or fetch children for lists of parents all at once with the in operator:

JavaScript
1// get all comments that don't have a post
2const commentsForPost = await api.comment.findMany({
3 filter: {
4 post: {
5 isSet: false,
6 },
7 },
8});
9
10// get all comments for posts 10, 11 and 12
11const commentsForPost = await api.comment.findMany({
12 filter: {
13 post: {
14 in: [10, 11, 12],
15 },
16 },
17});

SingleEnumFilter 

The SingleEnumFilter type provides a filter definition for the enum field type when the selection of multiple options is not allowed.

The SingleEnumFilter type has the following fields:

SingleEnumFilter fields
isSet: Boolean
equals: String
notEquals: String
in: [String]

An example of a SingleEnumFilter used to fetch all JIRA tickets that are not in the backlog:

JavaScript
1// get all tickets that do not have a status of "backlog"
2await api.tickets.findMany({
3 filter: {
4 status: {
5 notEquals: "backlog",
6 },
7 },
8});

MultiEnumFilter 

The MultiEnumFilter type provides a filter definition for the enum field type when the selection of multiple options is allowed.

The MultiEnumFilter type has the following fields:

MultiEnumFilter fields
isSet: Boolean
equals: [String]
notEquals: [String]
contains: [String]

The contains filter field is unique to the MultiEnumFilter type. If an enum field allows for the selection of multiple values, contains can be used to match against a combination of values in a single query. contains checks to see if all provided filter values are present, but does not perform an exact match - for exact matching, use equals.

For example, if an enum field is used to keep track of the means of communication a Client has signed up for, it might contain the following options: "Sms", "Email", "Phone". Multiple options can be selected at once. If I want to filter all Clients that allow for "Sms" and "Email" communication, I could use the following filter:

Example of a contains filter on an Enum field with multiple selections enabled
JavaScript
1await api.client.findMany({
2 filter: {
3 communication: {
4 contains: ["Sms", "Email"],
5 },
6 },
7});

This will return all Client records that have both the "Sms" and "Email" options selected. Because this is not an exact match, this will also include Client records that have all 3 options selected.

JSONFilter 

The JSONFilter type provides a filter definition for the json field type.

The JSONFilter type has the following fields:

JSONFilter fields
1isSet: Boolean
2equals: JSON
3in: [JSON]
4notIn: [JSON]
5notEquals: JSON
6matches: JSON

The matches filter field is unique to the JSONFilter type and allows you to perform a partial match on json fields. For example:

JavaScript
1await api.systemSetup.findMany({
2 filter: {
3 configuration: {
4 matches: { foo: "bar" },
5 },
6 },
7});

This will return all of the System Setup model's records that have the name/value pair { foo: "bar" } in the configuration field. Examples of this include configuration: { foo: "bar" } as well as configuration: { foo: "bar", fizz: "buzz" }. Both of these records would be returned by the above query.

VectorFilter 

The VectorFilter type provides filtering on vector fields storing lists of floats (vectors) in the database. Vector filters can be used to return only records that have a vector field similar to an input vector.

The VectorFilter type has the following fields:

JSONFilter fields
1isSet: Boolean
2equals: Vector
3l2Distance: {
4 to: vector
5 lessThan: Float
6 lessThanOrEqual: Float
7 greaterThan: Float
8 greaterThanOrEqual: Float
9}
10cosineSimilarity: {
11 to: vector
12 lessThan: Float
13 lessThanOrEqual: Float
14 greaterThan: Float
15 greaterThanOrEqual: Float
16}

For example, to filter to only records that have a vector field named embedding set, you can use the isSet filter:

JavaScript
1await api.someModel.findMany({
2 filter: {
3 embedding: {
4 isSet: true,
5 },
6 },
7});

Vector distance filters 

vector fields can be filtered to only return records with a vector that is similar to an input vector. These filter types accept an input vector in the to field, and then a distance or similarity threshold. Each stored vector in your model's field is compared to the input vector to compute a distance or similarity, and then only the vectors which have a distance or similarity passing the input threshold are returned.

For example, to return only vectors that have a cosine similarity of at least 0.8 to a given input vector, you can use the cosineSimilarity filter:

JavaScript
1await api.someModel.findMany({
2 filter: {
3 embedding: {
4 cosineSimilarity: {
5 to: [1, 2, 3],
6 greaterThan: 0.8,
7 },
8 },
9 },
10});

This will return records from the someModel model that have a vector stored in the embedding field that is similar to the input vector [1, 2, 3].

When filtering vectors, the input vector in the to field must have the same dimensions (length) as the stored vector. Trying to filter by a vector with different dimensions will throw an error and not return any data.

To return only vectors that have an L2 (Euclidian) distance of less than 1 to a given input vector, you can use the l2Distance filter:

JavaScript
1await api.someModel.findMany({
2 filter: {
3 embedding: {
4 l2Distance: {
5 to: [1, 0, 1],
6 lessThanOrEqualTo: 1,
7 },
8 },
9 },
10});

When searching for vectors that are close to an input vector, it is recommended to use the cosineSimilarity sort, instead of the filter. The closest vectors to a given input can be found by sorting by cosineSimilarity descending, which will return the closest vectors first.

See more details in the Sorting by vector distance section.

StateFilter 

The StateFilter type provides a filter definition for the record state field type. This field type is currently only in use on the Shopify Shop model that is added to your Gadget project when you set up a Shopify connection.

The StateFilter type has the following fields:

StateFilter fields
isSet: Boolean
inState: String

The inState filter field is unique to the StateFilter type. You need to use a dot-separated string of states to filter with inState, for example:

Example of an inState filter used on the Shopify Shop model
JavaScript
await api.shopifyShop.findMany({
filter: {
state: { inState: "created.installed" },
},
});

Filtering nulls with isSet 

All the filter types allow you to filter for records where the value is set to something or set to null using the isSet filter. { isSet: true } will only return records where the value is set (is not null), and { isSet: false } will only return records where the value is null.

For example, we could filter a Post model to only find records where the publishDate is set to something:

Example of an isSet filter used on the Post model
JavaScript
await api.post.findMany({
filter: {
publishDate: { isSet: true },
},
});

Or filter the Shopify Order model to find records where the customMetafield field is currently null:

Example of an isSet filter used on the ShopifyOrder model
JavaScript
await api.shopifyOrder.findMany({
filter: {
customMetafield: { isSet: false },
},
});

Unfilterable fields 

Currently, Gadget doesn't support filtering for the following field types:

  • file
  • encrypted string
  • password
  • has many
  • has one
  • has many through

You cannot filter a model by the properties of a related model.

If you have two related models, such as Post and Author for a blog app where each Author has many Post and you want to get the published blog posts for authors who live in Canada (filter Author by Posts's "published" property), you need to write two separate queries:

Filter across relationships
JavaScript
1const canadianAuthors = await api.author.findMany({
2 filter: {
3 country: { equals: "Canada" },
4 },
5});
6const canadianAuthorIds = canadianAuthors.map((author) => author.id);
7const posts = await api.post.findMany({
8 filter: {
9 AND: [
10 { authorId: { in: canadianAuthorIds } },
11 { isPublished: { equals: true } },
12 ],
13 },
14});

You can filter related models by the related model's attributes using GraphQL. This is not yet supported in the JavaScript client! For example, you could filter for Author who live in Canada and include their blog Posts that are longer than 500 words:

GraphQL
1query {
2 authors(filter: { country: { equals: "Canada" } }) {
3 edges {
4 node {
5 id
6 name
7 country
8 posts(filter: { wordCount: { greaterThan: 500 } }) {
9 edges {
10 node {
11 id
12 title
13 wordCount
14 }
15 }
16 }
17 }
18 }
19 }
20}

This would return all authors along with their blog posts that have a word count greater than 500.

There is one exception to this rule: you can filter from the belongs to side of a relationship on the id of the related model. This means you can use isSet, equals, or any of the other filter operations that would be valid on a id.

For example, if you simply wanted to filter for published Postss that have Authors without filtering on the Author's country you could use the following query:

Filter across relationships on the "belongsTo" side of the relationship
JavaScript
1// this will only apply the `author` filter to the ID field on author!
2const posts = await api.post.findMany({
3 filter: {
4 isPublished: { equals: true },
5 author: { isSet: true },
6 },
7});