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.



