SalakCode SalakCode
Networking

GraphQL

A query language for your API

Intermediate
api query data networking

Definition

GraphQL is a query language for APIs that allows clients to request exactly the data they need. Unlike REST where the server defines the response structure, GraphQL lets the client specify the shape of the response, eliminating over-fetching and under-fetching.

GraphQL vs REST

// REST: Multiple endpoints, fixed responses
GET /users/1
GET /users/1/posts
GET /users/1/friends

// GraphQL: Single endpoint, flexible queries
POST /graphql
{
  user(id: 1) {
    name
    posts { title }
    friends { name }
  }
}

Basic Concepts

Queries

// Request specific fields
const GET_USER = `
  query GetUser($id: ID!) {
    user(id: $id) {
      id
      name
      email
      posts {
        title
        publishedAt
      }
    }
  }
`;

// Execute query
fetch('/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: GET_USER,
    variables: { id: '1' }
  })
});

Mutations

// Create, update, delete operations
const CREATE_POST = `
  mutation CreatePost($input: CreatePostInput!) {
    createPost(input: $input) {
      id
      title
      content
    }
  }
`;

fetch('/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: CREATE_POST,
    variables: {
      input: {
        title: 'My Post',
        content: 'Hello World'
      }
    }
  })
});

Client Libraries

Apollo Client

import { ApolloClient, InMemoryCache, gql } from '@apollo/client';

const client = new ApolloClient({
  uri: 'https://api.example.com/graphql',
  cache: new InMemoryCache()
});

// Simple query
client.query({
  query: gql`
    query GetUsers {
      users {
        id
        name
      }
    }
  `
}).then(result => console.log(result.data));

// React hook
import { useQuery, useMutation } from '@apollo/client';

function UserList() {
  const { loading, error, data } = useQuery(gql`
    query GetUsers {
      users {
        id
        name
      }
    }
  `);
  
  if (loading) return <p>Loading...</p>;
  if (error) return <p>Error: {error.message}</p>;
  
  return (
    <ul>
      {data.users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  );
}

Relay

// Facebook's GraphQL client
import { graphql, useFragment } from 'react-relay';

const UserComponent = ({ user }) => {
  const data = useFragment(
    graphql`
      fragment UserComponent_user on User {
        name
        email
      }
    `,
    user
  );
  
  return <div>{data.name}</div>;
};

Advanced Features

Fragments

// Reusable field selections
const UserFields = gql`
  fragment UserFields on User {
    id
    name
    email
    avatar
  }
`;

const GET_USER_WITH_POSTS = gql`
  ${UserFields}
  
  query GetUser($id: ID!) {
    user(id: $id) {
      ...UserFields
      posts {
        title
      }
    }
  }
`;

Subscriptions

// Real-time updates
const COMMENTS_SUBSCRIPTION = gql`
  subscription OnCommentAdded($postId: ID!) {
    commentAdded(postId: $postId) {
      id
      content
      author {
        name
      }
    }
  }
`;

function Comments({ postId }) {
  const { data } = useSubscription(COMMENTS_SUBSCRIPTION, {
    variables: { postId }
  });
  
  return (
    <div>
      {data?.commentAdded && (
        <div>New comment: {data.commentAdded.content}</div>
      )}
    </div>
  );
}

Error Handling

const { loading, error, data } = useQuery(GET_USER, {
  errorPolicy: 'all' // Return partial data with errors
});

if (error) {
  return (
    <div>
      {error.graphQLErrors.map(({ message }, i) => (
        <span key={i}>{message}</span>
      ))}
    </div>
  );
}

Best Practices

Caching

const client = new ApolloClient({
  cache: new InMemoryCache({
    typePolicies: {
      Query: {
        fields: {
          users: {
            // Merge cached results
            merge(existing, incoming) {
              return incoming;
            }
          }
        }
      }
    }
  })
});

Optimistic UI

const [addTodo] = useMutation(ADD_TODO, {
  optimisticResponse: {
    addTodo: {
      id: 'temp-id',
      text: newTodoText,
      completed: false,
      __typename: 'Todo'
    }
  },
  update(cache, { data: { addTodo } }) {
    const { todos } = cache.readQuery({ query: GET_TODOS });
    cache.writeQuery({
      query: GET_TODOS,
      data: { todos: [...todos, addTodo] }
    });
  }
});
Key Takeaway

GraphQL provides precise data fetching through a single endpoint. Use Apollo Client or Relay for production apps, leverage fragments for reusability, and implement optimistic UI for better perceived performance. The strong typing enables better tooling and fewer runtime errors.

Resources

Related Topics