GraphQL Subscriptions for real-time data are being adopted more and more widely in frontend development. Drawing on real-world projects, this article takes a deep dive into their core principles and best practices.
Basic Usage
Here are a few ways we can improve on this:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
const httpLink = createHttpLink({ uri: '/graphql' })
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token')
return { headers: { ...headers, authorization: `Bearer ${token}` } }
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
users: { keyArgs: ['filter'], merge: (e, i) => ({ ...i, edges: [...(e?.edges||[]), ...i.edges] }) }
}
}
}
})
})
This approach has been running stably in production for over six months and has proven itself in real use.
Advanced Usage
Let's start with the basic implementation:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
const httpLink = createHttpLink({ uri: '/graphql' })
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token')
return { headers: { ...headers, authorization: `Bearer ${token}` } }
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
users: { keyArgs: ['filter'], merge: (e, i) => ({ ...i, edges: [...(e?.edges||[]), ...i.edges] }) }
}
}
}
})
})
This snippet shows the basic usage. In a real project you'll also need to account for error handling and edge cases.
Practical Cases
Building on this, we can optimize further:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
const httpLink = createHttpLink({ uri: '/graphql' })
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token')
return { headers: { ...headers, authorization: `Bearer ${token}` } }
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
users: { keyArgs: ['filter'], merge: (e, i) => ({ ...i, edges: [...(e?.edges||[]), ...i.edges] }) }
}
}
}
})
})
This pattern is especially useful in large projects and can significantly lower maintenance costs.
Performance Optimization
Real-world usage is a bit more involved:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
const httpLink = createHttpLink({ uri: '/graphql' })
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token')
return { headers: { ...headers, authorization: `Bearer ${token}` } }
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
users: { keyArgs: ['filter'], merge: (e, i) => ({ ...i, edges: [...(e?.edges||[]), ...i.edges] }) }
}
}
}
})
})
This improves both the testability and extensibility of the code.
Common Traps
Here is a complete example:
import { ApolloClient, InMemoryCache, createHttpLink } from '@apollo/client'
import { setContext } from '@apollo/client/link/context'
const httpLink = createHttpLink({ uri: '/graphql' })
const authLink = setContext((_, { headers }) => {
const token = localStorage.getItem('token')
return { headers: { ...headers, authorization: `Bearer ${token}` } }
})
const client = new ApolloClient({
link: authLink.concat(httpLink),
cache: new InMemoryCache({
typePolicies: {
Query: {
fields: {
users: { keyArgs: ['filter'], merge: (e, i) => ({ ...i, edges: [...(e?.edges||[]), ...i.edges] }) }
}
}
}
})
})
Mind edge-case handling — it is critical in production.
Summary
- Understanding the underlying principles matters more than memorizing APIs.
- Always verify compatibility before shipping to production.
- In team collaboration, conventions and documentation matter more than the technology itself.
- Keep an eye on the community — technical solutions need to keep iterating.
- Don't adopt new tech just for the sake of using it.
