How to Query Your API with GraphQL: A Practical Tutorial

Learn how to query your API with GraphQL using practical examples of queries, mutations, variables, fragments, and aliases so clients can fetch exactly the data they need from the Pet Store API in a single flexible endpoint.

TRY NANO BANANA FOR FREE

How to Query Your API with GraphQL: A Practical Tutorial

TRY NANO BANANA FOR FREE
Contents

GraphQL lets clients request exactly the data they need. This tutorial shows how to write GraphQL queries and mutations, using the Pet Store API as an example.

The GraphQL Endpoint

GraphQL uses a single endpoint for all operations. Send POST requests with your query:

curl -X POST "https://api.petstoreapi.com/v1/graphql" \
  -H "Content-Type: application/json" \
  -d '{
    "query": "{ pets { name } }"
  }'

All queries and mutations go through this one endpoint.

Writing Your First Query

A basic query fetches data. The structure matches your response:

query {
  pets {
    name
  }
}

This returns all pets with their names:

{
  "data": {
    "pets": [
      { "name": "Fluffy" },
      { "name": "Buddy" },
      { "name": "Max" }
    ]
  }
}

Add more fields:

query {
  pets {
    id
    name
    status
    category {
      name
    }
  }
}

The response includes exactly what you requested. No more, no less.

Query with Arguments

Filter results using arguments:

query {
  pet(id: "123") {
    name
    status
    category {
      name
    }
  }
}

This fetches a single pet by ID. GraphQL validates that the pet exists and returns exactly those fields.

Filter with variables:

const query = `
  query GetPet($petId: ID!) {
    pet(id: $petId) {
      name
      status
    }
  }
`;

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

Variables make queries reusable. Write one query, pass different IDs.

Filtering Results

GraphQL arguments filter collections:

query {
  pets(status: AVAILABLE, limit: 10) {
    id
    name
    category {
      name
    }
  }
}

This returns only available pets, limited to 10 results.

Nested Queries

Fetch related data in one request:

query {
  pet(id: "123") {
    name
    orders {
      id
      total
      items {
        quantity
        product {
          name
          price
        }
      }
    }
  }
}

One query fetches the pet, its orders, order items, and product details. No N+1 requests.

Mutations: Changing Data

Mutations modify data. The syntax resembles queries but uses the mutation keyword:

mutation {
  createPet(input: {
    name: "Charlie"
    status: AVAILABLE
    categoryId: "1"
  }) {
    id
    name
    status
  }
}

The response returns the created pet. You see the result of your change.

Updating data:

mutation {
  updatePet(id: "123", input: {
    name: "Charlie Jr."
    status: SOLD
  }) {
    id
    name
    status
  }
}

Deleting data:

mutation {
  deletePet(id: "123")
}

The response is simply true or false for deletions.

Using Variables in Mutations

Pass input as variables:

const mutation = `
  mutation CreatePet($input: CreatePetInput!) {
    createPet(input: $input) {
      id
      name
    }
  }
`;

fetch('/graphql', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    query: mutation,
    variables: {
      input: {
        name: "Charlie",
        status: "AVAILABLE",
        categoryId: "1"
      }
    }
  })
});

Variables keep mutations clean and reusable.

Aliases

Aliases let you fetch the same field with different arguments:

query {
  available: pets(status: AVAILABLE) {
    name
  }
  sold: pets(status: SOLD) {
    name
  }
}

The response uses your alias names:

{
  "data": {
    "available": [{ "name": "Fluffy" }],
    "sold": [{ "name": "Buddy" }]
  }
}

Fragments

Fragments define reusable field sets:

fragment PetFields on Pet {
  id
  name
  status
  category {
    name
  }
}

query {
  pet(id: "123") {
    ...PetFields
  }
  pets {
    ...PetFields
  }
}

Define once, use everywhere. Fragments reduce repetition.

Error Handling

GraphQL returns errors in an errors array:

{
  "data": { "pet": null },
  "errors": [
    {
      "message": "Pet not found",
      "locations": [{ "line": 2, "column": 3 }],
      "path": ["pet"]
    }
  ]
}

Check both data and errors in your response handler.

Real-World Example

Build a pet management interface:

// Fetch pet with orders
const GET_PET_WITH_ORDERS = `
  query GetPetWithOrders($petId: ID!) {
    pet(id: $petId) {
      ...PetBasicFields
      category {
        ...CategoryFields
      }
      orders {
        id
        total
        createdAt
      }
    }
  }

  fragment PetBasicFields on Pet {
    id
    name
    status
  }

  fragment CategoryFields on Category {
    id
    name
  }
`;

// Add a new pet
const ADD_PET = `
  mutation AddPet($input: CreatePetInput!) {
    createPet(input: $input) {
      ...PetBasicFields
    }
  }

  fragment PetBasicFields on Pet {
    id
    name
    status
  }
`;

// Update pet status
const UPDATE_STATUS = `
  mutation UpdateStatus($id: ID!, $status: PetStatus!) {
    updatePet(id: $id, input: { status: $status }) {
      id
      name
      status
    }
  }
`;

async function getPet(id) {
  const result = await query(GET_PET_WITH_ORDERS, { petId: id });
  return result.data.pet;
}

async function addPet(petData) {
  const result = await query(ADD_PET, { input: petData });
  return result.data.createPet;
}

async function updateStatus(id, status) {
  const result = await query(UPDATE_STATUS, { id, status });
  return result.data.updatePet;
}

This structure keeps queries organized and reusable.

Pet Store API GraphQL

The Pet Store API provides a complete GraphQL schema. Query pets, orders, customers, and inventory. Mutations handle CRUD operations.

Explore the schema at docs.petstoreapi.com/graphql. The interactive playground lets you test queries before implementing.

GraphQL gives your clients control over data shape. Fetch exactly what you need, no more, no less. The flexible query language adapts to any frontend requirement.