GraphQL
03 / 04

Queries, Mutations & Apollo Client

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>

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free