# Queries

Fetching data involves executing query operations using standard GraphQL documents. You can learn more about queries and GraphQL documents here and practice running queries in the playground.

# Executing a query

# GraphQL document

Let's take this example GraphQL document throughout this section:

query getUsers {
  users {
    id
    firstname
    lastname
    email
  }
}

TIP

It is recommended to give a name to your GraphQL operations (here getUsers), so it is easier to find them in the Apollo Client devtools.

This query would return a data object with an array of users with their id, firstname, lastname and email. It could look like this:

{
  "data": {
    "users": [
      {
        "id": "abc",
        "firstname": "James",
        "lastname": "Holden",
        "email": "james.holden@roci.com"
      },
      {
        "id": "def",
        "firstname": "Naomi",
        "lastname": "Nagata",
        "email": "naomi.nagata@roci.com"
      }
    ]
  }
}

You may ask: why is there a nested users property on data? Why isn't the array directly on data?

This is because you can select multiple root fields in a GraphQL operation:

query getCatsAndDogs {
  cats {
    id
  }

  dogs {
    id
  }
}

In this case, the result could look like this:

{
  "data": {
    "cats": [
      { "id": "abc" },
      { "id": "def" }
    ],
    "dogs": [
      { "id": "ghi" },
      { "id": "jkl" }
    ]
  }
}

There can also be other optional properties on the result alongside data:

  • errors : an array of errors returned by the server
  • extensions : additional informations such as execution timings

# useQuery

The main composition function used to execute queries is useQuery. In your component, start by importing it:

<script>
import { useQuery } from '@vue/apollo-composable'

export default {
  setup () {
    // Your data & logic here...
  },
}
</script>

You can use useQuery in your setup option and pass it a GraphQL document as the first parameter. Then retrieve the query result:



 



 
 
 
 
 
 
 
 
 
 




<script>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)
  },
}
</script>

Note that result here is a Ref holding the data from the result returned by Apollo.

If you want to directly access the data object, use result.value:


 
















 
 
 




<script>
import { watch } from '@vue/composition-api'
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    watch(() => {
      console.log(result.value)
    })
  },
}
</script>

In this example, you could also watch the Ref directly:



















 
 





<script>
import { watch } from '@vue/composition-api'
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    watch(result, value => {
      console.log(value)
    })
  },
}
</script>

Let's expose our result in the template:


















 
 
 




 
 
 
 
 
 
 

<script>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    return {
      result,
    }
  },
}
</script>

<template>
  <ul>
    <li v-for="user of result.users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

Beware that result may not contain your data at all time! It will initially be undefined until the query successfully completes. So it's a good idea to add a conditionnal before rendering the data:


 






<template>
  <ul v-if="result && result.users">
    <li v-for="user of result.users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

# Query status

# Loading state

Alongside result, useQuery returns loading, a boolean Ref tracking the loading state of the query:







 












 






 

 






<script>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result, loading } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    return {
      result,
      loading,
    }
  },
}
</script>

<template>
  <div v-if="loading">Loading...</div>

  <ul v-else-if="result && result.users">
    <li v-for="user of result.users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

# Error

There is also an error Ref that holds any error that may occur during the request:







 













 








 








<script>
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result, loading, error } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    return {
      result,
      loading,
      error,
    }
  },
}
</script>

<template>
  <div v-if="loading">Loading...</div>

  <div v-else-if="error">Error: {{ error.message }}</div>

  <ul v-else-if="result && result.users">
    <li v-for="user of result.users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

# useResult

The sister composition function useResult is available alongside useQuery to facilitate usage of the query result.

# Result picking

The first useful feature of useResult is picking one object from the result data. To do so, pass the result data as the first parameter, and a picking function as the third parameter:


 















 


 












 
 





<script>
import { useQuery, useResult } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result, loading, error } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    const users = useResult(result, null, data => data.users)

    return {
      users,
      loading,
      error,
    }
  },
}
</script>

<template>
  <div v-if="loading">Loading...</div>

  <div v-else-if="error">Error: {{ error.message }}</div>

  <ul v-else-if="users">
    <li v-for="user of users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

This is very useful if the data relevant to your component is nested in the query:

const { result } = useQuery(gql`
  query getMessages {
    currentUser {
      messages {
        id
        text
      }
    }
  }
`)

const messages = useResult(result, null, data => data.currentUser.messages)

Another perk of useResult is that the picking function will silence errors inside the picking function. For example, if result.value is undefined, you don't have to add additional checks:

// You don't need to do this!
const messages = useResult(result, null, data => data && data.currentUser && data.currentUser.messages)

// Instead do this:
const messages = useResult(result, null, data => data.currentUser.messages)

TIP

Don't forget that messages.value can still be null until the query successfully completes!

Another use case where useResult proves to be very useful is when you have multiple objects on the result data:

const { result } = useQuery(gql`
  query getUsersAndPosts {
    users {
      id
      email
    }

    posts {
      id
      title
    }
  }
`)

const users = useResult(result, null, data => data.users)

const posts = useResult(result, null, data => data.posts)

Look how we cleanly separated the result data into two different Ref!

# Automatic picking

If there is only one object in the data, useResult will automatically try to pick the object:












 

const { result, loading } = useQuery(gql`
  query getUsers {
    users {
      id
      firstname
      lastname
      email
    }
  }
`)

const users = useResult(result)

Here users.value will be the users array retrieved from our server.

# Default value

Let's say we want to sort our users on their last names:


 
















 
 
 


 









 
 





<script>
import { computed } from '@vue/composition-api'
import { useQuery } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result, loading } = useQuery(gql`
      query users {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    const sortedUsers = computed(() => result.value.users.sort(
      (a, b) => a.lastname.localeCompare(b.lastname)
    ))

    return {
      sortedUsers,
      loading,
    }
  },
}
</script>

<template>
  <div v-if="loading">Loading...</div>

  <ul v-else-if="sortedUsers">
    <li v-for="user of sortedUsers" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>
  </ul>
</template>

Here we will run into an error because result.value can be undefined (and potentially result.value.users can also be undefined). So the sort method will throw an error when called in our computed property.

We could add checks, but it can rapidly become tedious:

const sortedUsers = computed(() => result.value && result.value.users ? result.value.users.sort(
  (a, b) => a.lastname.localeCompare(b.lastname)
) : [])

We can further simplify this with useResult:

 

 

 

const users = useResult(result) // Automatically picked

const sortedUsers = computed(() => users.value ? users.value.sort(
  (a, b) => a.lastname.localeCompare(b.lastname)
) : [])

But we can eliminate the conditional entirely if we pass a default value as the 2nd parameter of useResult:

 

 

 

const users = useResult(result, []) // Defaults to an empty array

const sortedUsers = computed(() => users.value.sort(
  (a, b) => a.lastname.localeCompare(b.lastname)
))

This is even more useful if we want to use the users array Ref in multiple places in our setup function!

# Variables

You can pass a variables object to the 2nd parameter of useQuery:

const { result } = useQuery(gql`
  query getUserById ($id: ID!) {
    user (id: $id) {
      id
      email
    }
  }
`, {
  id: 'abc-abc-abc',
})

# Variables Ref

You can change them later by retrieving their variables Ref:

 










 
 
 
 
 

const { result, variables } = useQuery(gql`
  query getUserById ($id: ID!) {
    user (id: $id) {
      id
      email
    }
  }
`, {
  id: 'abc-abc-abc',
})

function selectUser (id) {
  variables.value = {
    id,
  }
}

TIP

This will re-fetch the query each time a property from the variables object changes.

Alternatively, you can pass a Ref directly:

import { ref } from '@vue/composition-api'
 
 
 








 







const variables = ref({
  id: 'abc-abc-abc',
})

const { result } = useQuery(gql`
  query getUserById ($id: ID!) {
    user (id: $id) {
      id
      email
    }
  }
`, variables)

function selectUser (id) {
  variables.value = {
    id,
  }
}

# Reactive object

You can also pass a reactive object:

import { reactive } from '@vue/composition-api'
 













 


const variables = reactive({
  id: 'abc-abc-abc',
})

const { result } = useQuery(gql`
  query getUserById ($id: ID!) {
    user (id: $id) {
      id
      email
    }
  }
`, variables)

function selectUser (id) {
  variables.id = id
}

This also means you can pass props from setup directly, since props is already a reactive object:

export default {
  props: ['id'],

  setup (props) {
    const { result } = useQuery(gql`
      query getUserById ($id: ID!) {
        user (id: $id) {
          id
          email
        }
      }
    `, props)

    return {
      result,
    }
  },
}

But beware if you add new props that aren't used in the GraphQL document, you will run into GraphQL validation errors!

# Variables function

Finally, you can pass variables as a function returning an object:












 
 
 







export default {
  props: ['id'],

  setup (props) {
    const { result } = useQuery(gql`
      query getUserById ($id: ID!) {
        user (id: $id) {
          id
          email
        }
      }
    `, () => ({
      id: props.id,
    }))

    return {
      result,
    }
  },
}

This variables function will be made reactive automatically, so whenever props.id changes, the variables object of the query will be updated.

This syntax is also useful if you want to use some Refs in the variables:

const id = ref('abc-abc-abc')

const { result } = useQuery(gql`
  query getUserById ($id: ID!) {
    user (id: $id) {
      id
      email
    }
  }
`, () => ({
  id: id.value
}))

function selectUser (id) {
  id.value = id
}

# Options

The third parameter of useQuery is an options object, used to configure your query.

Like variables, you can pass a Ref, a reactive object or a function that will automatically be reactive.

Using a Ref:

const options = ref({
  fetchPolicy: 'cache-first',
})

const { result } = useQuery(gql`
  query getUsers {
    users {
      id
      email
    }
  }
`, null, options)

Using a reactive object:

const options = reactive({
  fetchPolicy: 'cache-first',
})

const { result } = useQuery(gql`
  query getUsers {
    users {
      id
      email
    }
  }
`, null, options)

Using a function that will automatically be reactive:

const fetchPolicy = ref('cache-first')

const { result } = useQuery(gql`
  query getUsers {
    users {
      id
      email
    }
  }
`, null, () => ({
  fetchPolicy: fetchPolicy.value
}))

See the API Reference for all the possible options.

# Disable a query

You can disable and re-enable a query with the enabled option:

const enabled = ref(false)

const { result } = useQuery(gql`
  ...
`, null, () => ({
  enabled: enabled.value,
}))

function enableQuery () {
  enabled.value = true
}

# Fetch Policy

The fetchPolicy option allows you to customize how the query will use the Apollo Client cache.

const { result } = useQuery(gql`
  ...
`, null, {
  fetchPolicy: 'cache-and-network',
})

Available values are:

  • cache-first (default): return result from cache. Only fetch from network if cached result is not available.
  • cache-and-network: return result from cache first (if it exists), then return network result once it's available.
  • cache-only: return result from cache if available, fail otherwise.
  • network-only: return result from network, fail if network call doesn't succeed, save to cache.
  • no-cache: return result from network, fail if network call doesn't succeed, don't save to cache.

# Updating cached results

When a query is completed, it will update the cache with the result data (depending on the fetch policy). This improves performance the next time the data needs to be rendered in your application and ensures that all components relying on a piece of data is always consistent.

However, you sometimes want to make sure that this data is up-to-date compared to the server.

# Polling

Polling means repeatedly calling the server to automatically update the query data.

You can enable polling with the pollInterval which will be the interval in ms between each requests repeatedly made to the server.

In this example, we will poll the server every second:

const { result } = useQuery(gql`
  ...
`, null, {
  pollInterval: 1000,
})

# Refetching

The other way is manually executing the query again in response to an event, as opposed to using a fixed interval.

This is done using the refetch function:







 
















 















 



<script>
import { useQuery, useResult } from '@vue/apollo-composable'
import gql from 'graphql-tag'

export default {
  setup () {
    const { result, loading, error, refetch } = useQuery(gql`
      query getUsers {
        users {
          id
          firstname
          lastname
          email
        }
      }
    `)

    const users = useResult(result)

    return {
      users,
      loading,
      error,
      refetch,
    }
  },
}
</script>

<template>
  <div v-if="loading">Loading...</div>

  <div v-else-if="error">Error: {{ error.message }}</div>

  <ul v-else-if="users">
    <li v-for="user of users" :key="user.id">
      {{ user.firstname }} {{ user.lastname }}
    </li>

    <button @click="refetch()">Refresh</button>
  </ul>
</template>

# Event hooks

useQuery returns event hooks allowing you to execute code when a specific event occurs.

# onResult

This is called whenever a new result is available.

const { onResult } = useQuery(...)

onResult(queryResult => {
  console.log(queryResult.data)
  console.log(queryResult.loading)
  console.log(queryResult.networkStatus)
  console.log(queryResult.stale)
})

You can pass the notifyOnNetworkStatusChange option to force the query to trigger a new resutl when the network status or error is updated:

useQuery(gql`
  ...
`, null, {
  notifyOnNetworkStatusChange: true,
})

# onError

It is triggered when an error occurs:

const { onError } = useQuery(...)

onError(error => {
  console.log(error.graphQLErrors)
  console.log(error.networkError)
})

You can use the logErrorMessages function from the @vue/apollo-util package to format the error in the browser console:

import { logErrorMessages } from '@vue/apollo-util'

const { onError } = useQuery(...)

onError(error => {
  logErrorMessages(error)
})

Example error:

Error log screenshot

If you are using Webpack or Vue CLI, it's a good idea to only use it in development:

import { logErrorMessages } from '@vue/apollo-util'

const { onError } = useQuery(...)

onError(error => {
  if (process.env.NODE_ENV !== 'production') {
    logErrorMessages(error)
  }
})

That way it will be dropped when compiling the project for production.