How to use Apollo Client in Angular

Apollo Client provides powerful GraphQL client with normalized caching, optimistic UI updates, and reactive queries for sophisticated data management in Angular. As the creator of CoreUI, a widely used open-source UI library, I’ve integrated Apollo Angular in enterprise applications throughout my 12 years of frontend development since 2014. The most effective approach is using apollo-angular package which provides Angular-specific Apollo Client integration with observables and dependency injection. This method enables automatic cache updates, real-time subscriptions via WebSocket, and efficient data normalization across components.

Install apollo-angular and configure Apollo Client with GraphQL endpoint and cache policy for reactive data fetching.

npm install @apollo/client apollo-angular graphql

Setup Apollo Client module:

import { ApplicationConfig, provideZoneChangeDetection } from '@angular/core'
import { provideHttpClient } from '@angular/common/http'
import { APOLLO_OPTIONS, ApolloModule } from 'apollo-angular'
import { HttpLink } from 'apollo-angular/http'
import { InMemoryCache, ApolloClientOptions } from '@apollo/client/core'

export function apolloOptionsFactory(httpLink: HttpLink): ApolloClientOptions<any> {
  return {
    link: httpLink.create({
      uri: 'https://api.example.com/graphql'
    }),
    cache: new InMemoryCache({
      typePolicies: {
        Query: {
          fields: {
            users: {
              merge(existing = [], incoming) {
                return incoming
              }
            }
          }
        }
      }
    }),
    defaultOptions: {
      watchQuery: {
        fetchPolicy: 'cache-and-network',
        errorPolicy: 'all'
      },
      query: {
        fetchPolicy: 'network-only',
        errorPolicy: 'all'
      },
      mutate: {
        errorPolicy: 'all'
      }
    }
  }
}

export const appConfig: ApplicationConfig = {
  providers: [
    provideZoneChangeDetection({ eventCoalescing: true }),
    provideHttpClient(),
    {
      provide: APOLLO_OPTIONS,
      useFactory: apolloOptionsFactory,
      deps: [HttpLink]
    }
  ]
}

Using Apollo in services:

import { Injectable } from '@angular/core'
import { Apollo, gql } from 'apollo-angular'
import { Observable, map } from 'rxjs'

interface User {
  id: string
  name: string
  email: string
  posts: Post[]
}

interface Post {
  id: string
  title: string
  content: string
}

const GET_USER = gql`
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
      posts {
        id
        title
        content
      }
    }
  }
`

const GET_USERS = gql`
  query GetUsers($limit: Int!, $offset: Int!) {
    users(limit: $limit, offset: $offset) {
      id
      name
      email
    }
  }
`

const CREATE_POST = gql`
  mutation CreatePost($title: String!, $content: String!) {
    createPost(title: $title, content: $content) {
      id
      title
      content
    }
  }
`

const UPDATE_USER = gql`
  mutation UpdateUser($id: ID!, $name: String!, $email: String!) {
    updateUser(id: $id, name: $name, email: $email) {
      id
      name
      email
    }
  }
`

const DELETE_POST = gql`
  mutation DeletePost($id: ID!) {
    deletePost(id: $id)
  }
`

@Injectable({
  providedIn: 'root'
})
export class UserService {
  constructor(private apollo: Apollo) {}

  getUser(id: string): Observable<User> {
    return this.apollo
      .watchQuery<{ user: User }>({
        query: GET_USER,
        variables: { id }
      })
      .valueChanges.pipe(
        map(result => result.data.user)
      )
  }

  getUsers(limit: number = 10, offset: number = 0): Observable<User[]> {
    return this.apollo
      .query<{ users: User[] }>({
        query: GET_USERS,
        variables: { limit, offset }
      })
      .pipe(
        map(result => result.data.users)
      )
  }

  createPost(title: string, content: string): Observable<Post> {
    return this.apollo
      .mutate<{ createPost: Post }>({
        mutation: CREATE_POST,
        variables: { title, content },
        update: (cache, { data }) => {
          // Update cache after mutation
          if (data?.createPost) {
            cache.modify({
              fields: {
                posts(existingPosts = []) {
                  const newPostRef = cache.writeFragment({
                    data: data.createPost,
                    fragment: gql`
                      fragment NewPost on Post {
                        id
                        title
                        content
                      }
                    `
                  })
                  return [...existingPosts, newPostRef]
                }
              }
            })
          }
        }
      })
      .pipe(
        map(result => result.data!.createPost)
      )
  }

  updateUser(id: string, name: string, email: string): Observable<User> {
    return this.apollo
      .mutate<{ updateUser: User }>({
        mutation: UPDATE_USER,
        variables: { id, name, email },
        optimisticResponse: {
          updateUser: {
            __typename: 'User',
            id,
            name,
            email
          }
        }
      })
      .pipe(
        map(result => result.data!.updateUser)
      )
  }

  deletePost(id: string): Observable<boolean> {
    return this.apollo
      .mutate<{ deletePost: boolean }>({
        mutation: DELETE_POST,
        variables: { id },
        update: (cache) => {
          cache.evict({ id: `Post:${id}` })
          cache.gc()
        }
      })
      .pipe(
        map(result => result.data!.deletePost)
      )
  }
}

Using Apollo service in components:

import { Component, OnInit } from '@angular/core'
import { CommonModule } from '@angular/common'
import { UserService } from './user.service'

@Component({
  selector: 'app-users-list',
  standalone: true,
  imports: [CommonModule],
  template: `
    <div class='users-container'>
      <h2>Users</h2>

      <div *ngIf='loading' class='loading'>
        Loading users...
      </div>

      <div *ngIf='error' class='error'>
        {{ error.message }}
      </div>

      <div class='users-grid'>
        <div *ngFor='let user of users' class='user-card'>
          <h3>{{ user.name }}</h3>
          <p>{{ user.email }}</p>
          <button (click)='viewUser(user.id)'>View Profile</button>
        </div>
      </div>

      <div class='pagination'>
        <button
          (click)='loadPrevious()'
          [disabled]='offset === 0'
        >
          Previous
        </button>
        <button (click)='loadNext()'>
          Next
        </button>
      </div>
    </div>
  `
})
export class UsersListComponent implements OnInit {
  users: any[] = []
  loading = false
  error: any = null
  limit = 10
  offset = 0

  constructor(private userService: UserService) {}

  ngOnInit() {
    this.loadUsers()
  }

  loadUsers() {
    this.loading = true
    this.error = null

    this.userService.getUsers(this.limit, this.offset).subscribe({
      next: (users) => {
        this.users = users
        this.loading = false
      },
      error: (err) => {
        this.error = err
        this.loading = false
      }
    })
  }

  loadNext() {
    this.offset += this.limit
    this.loadUsers()
  }

  loadPrevious() {
    this.offset = Math.max(0, this.offset - this.limit)
    this.loadUsers()
  }

  viewUser(id: string) {
    // Navigate to user profile
  }
}

Here the apollo-angular package provides Apollo Client integration with Angular’s dependency injection and RxJS observables. The InMemoryCache normalizes GraphQL responses by type and ID for efficient caching. The gql tag parses GraphQL query strings into document nodes for Apollo Client. The watchQuery creates observable that emits on cache updates and network responses. Mutations with update function manually modify cache after successful mutation. Optimistic responses update UI immediately before server response arrives. The cache.evict removes specific entities from normalized cache after deletion.

Best Practice Note:

This is the Apollo Client setup we use in CoreUI Angular applications requiring sophisticated GraphQL data management with caching and real-time updates. Define GraphQL fragments for reusable field sets across queries, implement pagination with fetchMore for infinite scrolling, use subscriptions with apollo-angular subscriptions for real-time data updates via WebSocket, and leverage Apollo DevTools browser extension for cache inspection and query debugging during development.


Speed up your responsive apps and websites with fully-featured, ready-to-use open-source admin panel templates—free to use and built for efficiency.


About the Author