Vue Apollo Documentation

repository·v5·Indexed 27 days ago

https://github.com/vuejs/apollo

Integration between Apollo Client and Vue.js for using GraphQL in Vue applications. This documentation covers the development version (v5) and the @vue/apollo-composable package designed for the Vue 3 Composition API and Apollo Client 3.x, including features like useLazyQuery, loading state tracking, and support for multiple Apollo clients.

Tokens
67.8K
Snippets
178
Records
334
Agent score
89%

What's inside Vue Apollo

  1. Introduction to Vue Apollo

    v5

    Vue Apollo is the official Apollo Client integration for Vue.js. It enables management of local and remote GraphQL data using Vue's reactivity system. Key features include:

    • Declarative data fetching: Use useQuery to write queries and receive reactive data.
    • Automatic caching: Leverages Apollo's normalized cache for instant repeated queries and entity synchronization.
    • Real-time updates: Supports subscriptions and @defer / @stream directives.
    • TypeScript support: Full integration with GraphQL Codegen.
    • Vue-native reactivity: Variables can be refs, reactive objects, or getters; query results are returned as refs for use in templates.
  2. Overview of Apollo and GraphQL for Vue.js

    v5

    Vue Apollo provides integration between Apollo Client and Vue.js. This repository contains the source code for the next version (v5) of Vue Apollo. The current stable version is v4.

    Key package:

    • @vue/apollo-composable: Provides Apollo and GraphQL integration using the Vue Composition API.
  3. Understand Apollo Client Caching and Normalization

    v5

    Apollo Client uses a local, normalized, in-memory cache. It stores objects in a flat lookup table keyed by __typename and a unique identifier (usually id).

    Key benefits:

    • Instant Data: If data is in the cache and the fetchPolicy allows, useQuery returns results immediately without a network request.
    • Automatic Reactivity: When a mutation updates an entity in the cache (e.g., updating a User:42), every active useQuery that reads that entity updates its reactive refs automatically. You do not need to manually invalidate queries or use refetchQueries for standard entity updates.
  4. Access the Apollo Cache

    v5

    You can access the ApolloCache instance in two primary ways within a Vue application:

    1. Inside a mutation update callback: The cache is passed as the first argument to the update function.
    2. Using useApolloClient: For use in event handlers, stores, route guards, or utilities, import useApolloClient to get the client instance, then access client.cache.
    // 1. Inside mutation update
    useMutation(CREATE_TODO, {
      update(cache, { data }) {
        // cache is the ApolloCache instance
      },
    })
    
    // 2. From useApolloClient
    import { useApolloClient } from '@vue/apollo-composable'
    const { client } = useApolloClient()
    client.cache.readQuery({ /* ... */ })
  5. When to use multiple Apollo clients

    v5

    Deciding whether to use multiple clients depends on your backend architecture. Note that each client has its own independent cache; queries against one client cannot see updates from another.

    SituationRecommended Pattern
    One API, different auth states (anonymous vs logged in)Use one client; set auth headers per request via context
    One API, different cache policiesUse one client; vary fetch policies per query
    Separate backends with different schemasUse multiple clients
    Same backend, different versions or federated subgraphsUse multiple clients
  6. Read errors in Queries, Mutations, and Subscriptions

    v5

    Depending on the composable used, errors are accessed differently:

    Queries

    Access errors via the current.error property.

    Mutations

    Access errors via the error ref.

    Subscriptions

    Access errors via the error ref, or register an onError callback for imperative handling.

  7. Use `useLazyQuery` for user-triggered queries

    v5

    Use useLazyQuery when query variables are not known upfront or when a query should only run in response to a specific user action (such as a button click, search submission, or opening a modal) rather than automatically on component mount.

    Comparison of query types:

    • useLazyQuery: Use when variables come from a user action.
    • useQuery({ enabled }): Use when variables are known but execution should be gated by a condition.
    • useQuery: Use when variables are known and the query should run immediately on mount.

    useLazyQuery starts in a disabled state. It exposes a load(variables?) function to trigger execution. After the initial load, it behaves like a standard useQuery where variables become reactive and results update automatically.

    import { TypedDocumentNode } from '@apollo/client'
    import { useLazyQuery } from '@vue/apollo-composable'
    import { ref } from 'vue'
    
    // ... gql definition ...
    
    const term = ref('')
    const { load, current } = useLazyQuery(SEARCH_USERS)
    
    async function search() {
      const result = await load({ term: term.value })
      console.log('Found users:', result?.users)
    }
    </script>
    
    <template>
      <form @submit.prevent="search">
        <input v-model="term">
        <button>Search</button>
      </form>
    
      <div v-if="current.loading">
        Searching...
      </div>
      <ul v-else-if="current.resultState === 'complete'">
        <li v-for="user in current.result.users" :key="user.id">
          {{ user.name }}
        </li>
      </ul>
    </template>
  8. Automatic Cache Updates for Existing Entities

    v5
    Apollo Client automatically updates the local cache if a mutation returns the modified entity including its __typename and its unique key field (e.g., id). Any active useQuery reading that specific entity will automatically re-emit with the new values. This works for 'edit existing entity' scenarios without any additional configuration.
  9. Create an Apollo Client instance

    v5

    Configure your Apollo Client instance by creating a dedicated file (e.g., src/apollo.ts). You will typically need to provide an HttpLink with your GraphQL endpoint URI and an InMemoryCache instance.

    // src/apollo.ts
    import { ApolloClient, HttpLink, InMemoryCache } from '@apollo/client'
    
    export const apolloClient = new ApolloClient({
      link: new HttpLink({ uri: 'http://localhost:4000/graphql' }),
      cache: new InMemoryCache(),
    })
  10. Identify scenarios requiring manual Cache Updates

    v5

    While Apollo Client handles most entity updates automatically, you must manually manage the cache (via Cache Updates or refetching) for the following scenarios:

    • Adding to a list: When a mutation creates a new item that should appear in an existing list query.
    • Removing from a list: When a mutation deletes an item from a list.
    • Cross-entity changes: When a mutation affects data that is not directly returned by the mutation itself.
    • Pagination merging: When combining multiple pages of results into a single list.
  11. Handle loading states after initial render

    v5

    The await useQuery() pattern only handles the initial component setup. Subsequent updates—such as variable changes, cache invalidations, refetch() calls, or polling—will not trigger the <Suspense> fallback.

    To handle these secondary loading states, you must continue to check current.loading or current.resultState within your template.

    <script setup lang="ts">
    import { gql } from '@apollo/client'
    import { useQuery } from '@vue/apollo-composable'
    
    const props = defineProps<{ userId: string }>()
    
    const { current } = await useQuery(
      gql`
        query GetUser($id: ID!) {
          user(id: $id) {
            id
            name
          }
        }
      `,
      {
        variables: () => ({ id: props.userId }),
      },
    )
    </script>
    
    <template>
      <div v-if="current.loading" class="loading-overlay">
        Updating...
      </div>
      <div v-if="current.resultState === 'complete'">
        {{ current.result.user.name }}
      </div>
    </template>