Queries, Mutations & Apollo Client Writing Queries # Basic query query GetUser { user(id: "1") { id name email } } # With variables query GetUser($id: ID!) { us…
Queries, Mutations & Apollo Client
Writing Queries
# Basic query
query GetUser {
user(id: "1") {
id
name
email
}
}
# With variables
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
posts(limit: 5) {
id
title
}
}
}
# Fragments — reusable field sets
fragment UserFields on User {
id
name
email
createdAt
}
query GetUsers {
users {
...UserFields
posts { title }
}
}
# Aliases — rename fields in response
query CompareUsers {
alice: user(id: "1") { name email }
bob: user(id: "2") { name email }
}
# Inline fragments — for interfaces/unions
query Search($q: String!) {
search(query: $q) {
... on User { id name email }
... on Post { id title author { name } }
... on Comment { id body }
}
}
# Mutation
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
name
email
}
}
# Subscription
subscription OnMessageAdded($channelId: ID!) {
messageAdded(channelId: $channelId) {
id
body
author { name }
}
}
Apollo Client (React)
import { gql, useQuery, useMutation, useLazyQuery } from '@apollo/client';
// Define queries
const GET_USER = gql`
query GetUser($id: ID!) {
user(id: $id) {
id
name
email
}
}
`;
const CREATE_USER = gql`
mutation CreateUser($input: CreateUserInput!) {
createUser(input: $input) {
id
name
}
}
`;
// useQuery — fetches on mount
function UserProfile({ id }: { id: string }) {
const { data, loading, error, refetch } = useQuery(GET_USER, {
variables: { id },
fetchPolicy: 'cache-first', // cache-first | network-only | cache-and-network
skip: !id, // skip if no id
pollInterval: 30000, // poll every 30s
});
if (loading) return <Spinner />;
if (error) return <Error message={error.message} />;
return <div>{data.user.name}</div>;
}
// useLazyQuery — fetches on demand
const [searchUsers, { data, loading }] = useLazyQuery(SEARCH_USERS, {
fetchPolicy: 'network-only',
});
// Call when needed:
searchUsers({ variables: { query: 'alice' } });
// useMutation
const [createUser, { loading: creating }] = useMutation(CREATE_USER, {
// Update cache after mutation
update(cache, { data }) {
cache.modify({
fields: {
users(existingUsers = []) {
const newUserRef = cache.writeFragment({
data: data.createUser,
fragment: gql`fragment NewUser on User { id name }`,
});
return [...existingUsers, newUserRef];
},
},
});
},
// Or simpler — refetch affected queries
refetchQueries: [{ query: GET_USERS }],
onCompleted: (data) => toast.success(`Created ${data.createUser.name}`),
onError: (error) => toast.error(error.message),
});
await createUser({ variables: { input: { name: 'Alice', email: 'a@b.com' } } });
Apollo Client Setup
import { ApolloClient, InMemoryCache, createHttpLink, ApolloProvider, from } from '@apollo/client';
import { setContext } from '@apollo/client/link/context';
import { onError } from '@apollo/client/link/error';
const httpLink = createHttpLink({ uri: '/api/graphql' });
const authLink = setContext((_, { headers }) => ({
headers: {
...headers,
authorization: localStorage.getItem('token')
? `Bearer ${localStorage.getItem('token')}`
: '',
},
}));
const errorLink = onError(({ graphQLErrors, networkError }) => {
if (graphQLErrors) {
graphQLErrors.forEach(({ message, locations, path }) =>
console.error(`[GraphQL error]: ${message}`)
);
}
if (networkError) console.error(`[Network error]: ${networkError}`);
});
const client = new ApolloClient({
link: from([errorLink, authLink, httpLink]),
cache: new InMemoryCache({
typePolicies: {
User: { keyFields: ['id'] },
Query: {
fields: {
users: { merge: false }, // replace array on refetch
},
},
},
}),
defaultOptions: {
watchQuery: { errorPolicy: 'all' },
},
});
// Wrap app
<ApolloProvider client={client}>
<App />
</ApolloProvider>