Skip to main content

Overview

Protocol: GraphQL over HTTPS Endpoint: https://api.petstoreapi.com/v1/graphql Query Language: GraphQL When to Use GraphQL:
  • ✅ Complex, nested data requirements
  • ✅ Mobile applications (reduce payload size)
  • ✅ Multiple resources in single request
  • ✅ Flexible, self-documenting API
  • ✅ Different clients need different fields
When NOT to Use GraphQL:
  • ❌ Simple CRUD operations (use REST instead)
  • ❌ File uploads (use REST instead)
  • ❌ Need HTTP caching (REST has better caching)

How GraphQL Works

Client                          GraphQL Server
    │                                  │
    ├──── Query ─────────────────────>│
    │  { pets { id name species } }   │
    │                                  │
    │                         ┌────────┴────────┐
    │                         │ Parse Query    │
    │                         │ Validate       │
    │                         │ Execute        │
    │                         └────────┬────────┘
    │                                  │
    │<──── JSON Response ─────────────┤
    │  { "data": { "pets": [...] } }  │
Key Characteristics:
  • Request exactly the data you need
  • Single request for multiple resources
  • Strongly typed schema
  • Introspection (self-documenting)
  • No overfetching or underfetching

Basic Queries

List Pets

query GetPets {
  pets {
    id
    name
    species
    breed
    ageMonths
    status
  }
}
Response:
{
  "data": {
    "pets": [
      {
        "id": "019b4132-70aa-764f-b315-e2803d882a24",
        "name": "Buddy",
        "species": "DOG",
        "breed": "Golden Retriever",
        "ageMonths": 24,
        "status": "AVAILABLE"
      }
    ]
  }
}

Get Single Pet

query GetPet($id: ID!) {
  pet(id: $id) {
    id
    name
    species
    breed
    ageMonths
    status
    adoptionFee
    currency
  }
}
Variables:
{
  "id": "019b4132-70aa-764f-b315-e2803d882a24"
}

Filter Pets

query GetAvailableDogs {
  pets(filter: {
    species: DOG
    status: AVAILABLE
    ageRange: { min: 12, max: 36 }
  }) {
    id
    name
    breed
    ageMonths
    adoptionFee
  }
}

Pagination

query GetPetsPaginated {
  pets(page: 1, limit: 20) {
    data {
      id
      name
      species
    }
    pagination {
      page
      limit
      totalItems
      totalPages
    }
  }
}

Mutations

Create Pet

mutation CreatePet {
  createPet(input: {
    name: "Buddy"
    species: DOG
    breed: "Golden Retriever"
    ageMonths: 24
    status: AVAILABLE
    adoptionFee: 150.00
    currency: USD
  }) {
    id
    name
    species
    status
    createdAt
  }
}

Update Pet

mutation UpdatePet($id: ID!) {
  updatePet(
    id: $id
    input: {
      status: PENDING
      ageMonths: 25
    }
  ) {
    id
    name
    status
    ageMonths
    updatedAt
  }
}

Delete Pet

mutation DeletePet($id: ID!) {
  deletePet(id: $id) {
    id
    name
    success
    message
  }
}

Nested Queries

Pet with Owner

query GetPetWithOwner {
  pet(id: "pet_fYrZzCW9E1WIOyGw") {
    id
    name
    species
    owner {
      id
      username
      email
      avatar {
        url
      }
    }
  }
}

Order with Items

query GetOrderWithItems {
  order(id: "019b4141-e5f3-a1b7-c4d8-9f6e2a5b8c1d") {
    id
    status
    total
    currency
    items {
      productId
      quantity
      price
      product {
        name
        description
      }
    }
    shippingAddress {
      street
      city
      state
      country
      postalCode
    }
  }
}

Authentication

Include JWT token in Authorization header:
curl -X POST https://api.petstoreapi.com/v1/graphql \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_JWT_TOKEN" \
  -d '{
    "query": "query GetPets { pets { id name species } }"
  }'

Code Examples

JavaScript (fetch)

async function graphqlQuery(query, variables = {}) {
  const response = await fetch('https://api.petstoreapi.com/v1/graphql', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${token}`
    },
    body: JSON.stringify({ query, variables })
  });

  const { data, errors } = await response.json();

  if (errors) {
    throw new Error(errors[0].message);
  }

  return data;
}

// Get pets
const data = await graphqlQuery(`
  query GetAvailablePets {
    pets(filter: { status: AVAILABLE }) {
      id
      name
      species
      adoptionFee
    }
  }
`);

console.log(data.pets);

JavaScript (Apollo Client)

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

const client = new ApolloClient({
  uri: 'https://api.petstoreapi.com/v1/graphql',
  cache: new InMemoryCache(),
  headers: {
    'Authorization': `Bearer ${token}`
  }
});

// Query
const GET_PETS = gql`
  query GetPets($species: PetSpecies) {
    pets(filter: { species: $species }) {
      id
      name
      species
      breed
    }
  }
`;

const { data } = await client.query({
  query: GET_PETS,
  variables: { species: 'DOG' }
});

// Mutation
const CREATE_PET = gql`
  mutation CreatePet($input: CreatePetInput!) {
    createPet(input: $input) {
      id
      name
      species
    }
  }
`;

const { data: newPet } = await client.mutate({
  mutation: CREATE_PET,
  variables: {
    input: {
      name: 'Buddy',
      species: 'DOG',
      ageMonths: 24
    }
  }
});

Python (gql)

from gql import gql, Client
from gql.transport.requests import RequestsHTTPTransport

transport = RequestsHTTPTransport(
    url='https://api.petstoreapi.com/v1/graphql',
    headers={'Authorization': f'Bearer {token}'}
)

client = Client(transport=transport, fetch_schema_from_transport=True)

# Query
query = gql('''
  query GetPets {
    pets {
      id
      name
      species
    }
  }
''')

result = client.execute(query)
print(result['pets'])

# Mutation
mutation = gql('''
  mutation CreatePet($input: CreatePetInput!) {
    createPet(input: $input) {
      id
      name
      species
    }
  }
''')

result = client.execute(mutation, variable_values={
    'input': {
        'name': 'Buddy',
        'species': 'DOG',
        'ageMonths': 24
    }
})

Advanced Features

Fragments

Reuse common fields:
fragment PetFields on Pet {
  id
  name
  species
  breed
  ageMonths
}

query GetPets {
  pets {
    ...PetFields
  }
}

query GetPet($id: ID!) {
  pet(id: $id) {
    ...PetFields
    adoptionFee
  }
}

Directives

Conditionally include fields:
query GetPet($id: ID!, $withFee: Boolean!) {
  pet(id: $id) {
    id
    name
    species
    adoptionFee @include(if: $withFee)
  }
}

Aliases

Rename fields in response:
query GetPets {
  dogs: pets(filter: { species: DOG }) {
    id
    name
  }
  cats: pets(filter: { species: CAT }) {
    id
    name
  }
}

Multiple Queries

query GetDashboardData {
  availablePets: pets(filter: { status: AVAILABLE }) {
    id
    name
  }
  pendingOrders: orders(filter: { status: PENDING }) {
    id
    total
  }
  recentAdoptions: adoptions(limit: 5) {
    id
    date
  }
}

Introspection

Query the schema:
query {
  __schema {
    types {
      name
      description
    }
  }
}
Get type details:
query {
  __type(name: "Pet") {
    name
    description
    fields {
      name
      type {
        name
      }
    }
  }
}

Best Practices

1. Use Specific Fields

# Good - specific fields
query {
  pets {
    id
    name
  }
}

# Bad - too many fields
query {
  pets {
    id
    name
    species
    breed
    ageMonths
    status
    adoptionFee
    currency
    description
    # ... many more fields
  }
}

2. Batch Queries

# Good - single request
query GetDashboard {
  stats { totalPets totalOrders }
  recentPets { id name }
  recentOrders { id total }
}

# Bad - multiple requests
# query 1: stats
# query 2: recentPets
# query 3: recentOrders

3. Use Fragments for Reuse

fragment PetSummary on Pet {
  id
  name
  species
  status
}

# Use in multiple queries
query { pets { ...PetSummary } }
query { pet(id: "123") { ...PetSummary } }

Error Handling

GraphQL errors are returned in the response:
{
  "data": {
    "pet": null
  },
  "errors": [
    {
      "message": "Pet not found",
      "path": ["pet"],
      "extensions": {
        "code": "PET_NOT_FOUND"
      }
    }
  ]
}
Handle errors in code:
const { data, errors } = await client.query({ query });

if (errors) {
  errors.forEach(error => {
    console.error(error.message);
  });
}

if (data?.pet) {
  // Process pet
}

Comparison with REST

FeatureGraphQLREST
Data FetchingExact fieldsFixed responses
RequestsSingle for multiple resourcesMultiple endpoints
OverfetchingNoYes (often)
UnderfetchingNoYes (n+1 problem)
CachingComplexHTTP caching built-in
SchemaStrongly typedVaries
DocumentationSelf-documentingSeparate (OpenAPI)

Interactive Documentation