Queries
Queries in GraphQL are used to fetch data without modifying it.
Retrieve the list of contacts
List queries are paginated. Always pass first explicitly: the default page size is not the same on every field, so leaving it out gives you a size you did not choose (contacts returns 50, which is also its maximum).
query Contacts($first: Int!) {
contacts(first: $first) {
edges {
node {
id
name
lastName
displayName
}
}
pageInfo {
hasNextPage
endCursor
total
}
}
}
{
"first": 25
}
The higher first is, the more the query costs. See query cost and pagination.
Retrieve the basic data of a single contact
query Contact($id: ID!) {
contact(id: $id) {
id
name
lastName
displayName
}
}
{
"id": "123"
}
Narrowing down a list
Fetching a page and filtering it in your own code wastes requests and query budget. List queries take a common set of arguments for doing the work on the server instead. Most lists accept all of them; check the API Reference for the exact set on a given field.
| Argument | Use it to |
|---|---|
search | Match a free-text term across the record's searchable fields. |
where | Filter on a column with an operator. |
whereHas | Filter on a related record. |
orderBy | Sort the results. |
trashed | Include or isolate deleted records. |
search
The simplest filter: a free-text term, matched the way the application's own search does.
query SearchContacts($term: String!) {
contacts(first: 25, search: $term) {
edges {
node {
id
displayName
}
}
}
}
{
"term": "acme"
}
where
where filters on a specific column. Each condition is a column, an operator and a value:
query Clients {
contacts(
first: 25
where: { column: IS_CLIENT, operator: EQ, value: true }
) {
edges {
node {
id
displayName
}
}
}
}
Columns come from a per-field enum (ContactsWhereColumns here), so only columns that are actually filterable are accepted. Operators include EQ, NEQ, GT, GTE, LT, LTE, LIKE, NOT_LIKE, IN and others - see SQLOperator in the reference.
Combine conditions with AND and OR:
query RecentClients($since: DateTimeTz!) {
contacts(
first: 25
where: {
AND: [
{ column: IS_CLIENT, operator: EQ, value: true }
{ column: CREATED_AT, operator: GTE, value: $since }
]
}
) {
edges {
node {
id
displayName
createdAt
}
}
}
}
whereHas
Where where filters on the record's own columns, whereHas filters on its relations. Give it a relation, the IDs you care about, and how they should match:
query TaggedContacts($tagIds: [ID!]) {
contacts(
first: 25
whereHas: [{ relation: TAGS, mode: ANY, ids: $tagIds }]
) {
edges {
node {
id
displayName
}
}
}
}
mode decides how the IDs are applied:
ANY- the relation includes at least one of the given IDs.ALL- the relation includes every one of them.NONE- the relation is absent entirely, andidsis ignored.
Passing several entries in the array narrows the result further; they are combined with AND.
orderBy
orderBy takes a list of clauses, applied in order:
query SortedContacts {
contacts(
first: 25
orderBy: [{ column: NAME, order: ASC }]
) {
edges {
node {
id
displayName
}
}
}
}
Sorting by more than one column is a matter of adding clauses; the first is the primary sort.
trashed
Deleting a record is reversible, so deleted records still exist. By default a list leaves them out. trashed changes that:
WITHOUT- only records that are not deleted. This is the default.WITH- deleted and undeleted records together.ONLY- only deleted records.
query DeletedContacts {
contacts(first: 25, trashed: ONLY) {
edges {
node {
id
displayName
deletedAt
}
}
}
}
ONLY is the query to run when you need to find something in order to restore it.
Asking for the same field twice
If you need one field under two different sets of arguments, give each an alias. Without one they would collide, because the response is keyed by field name:
query ContactSplit {
clients: contacts(first: 10, where: { column: IS_CLIENT, operator: EQ, value: true }) {
edges {
node {
id
displayName
}
}
}
others: contacts(first: 10, where: { column: IS_CLIENT, operator: EQ, value: false }) {
edges {
node {
id
displayName
}
}
}
}
The response then carries data.clients and data.others separately.
Reusing a selection
When the same selection appears more than once, a fragment saves repeating it and keeps the two copies from drifting apart:
fragment ContactFields on Contact {
id
name
lastName
displayName
}
query ContactSplit {
clients: contacts(first: 10, where: { column: IS_CLIENT, operator: EQ, value: true }) {
edges {
node {
...ContactFields
}
}
}
others: contacts(first: 10, where: { column: IS_CLIENT, operator: EQ, value: false }) {
edges {
node {
...ContactFields
}
}
}
}
Fragments do not reduce query cost - the fields still count once per place the fragment is used - but they do keep a long document readable and under the size limit.