GraphQL.com

LearnTutorialsCommunityEventsFAQs  

Table of contents

    GRAPHQL BASICS

  • What is GraphQL?
  • The Query
  • Introducing Types
  • Scalars, Objects, and Lists
  • Nullability
  • Querying between TypesFields as object typesQuerying for data
  • Schema
  • Enums
  • Interfaces & Unions
  • Arguments
  • Mutations


Try GraphOSThe GraphQL developer platform

Querying between Types

Using object types, fields, and the all-important Query type, we can define how we ask a GraphQL service for data and what's available to ask for. But we don't start to see the graph in GraphQL until we can model and traverse the relationships between our types. In the section ahead, we'll look at how to represent the connections between our object types and query them altogether.

Fields as object types

In addition to returning scalar type data, fields on an object type can return information about another object type.

Consider the two types defined below.

type Fruit {
id: ID!
name: String!
quantity: Int!
price: Int!
averageWeight: Float
hasEdibleSeeds: Boolean
nutrients: [String]
}
type Stall {
id: ID!
name: String!
stallNumber: String!
}

We have a Fruit type, which defines properties that can be applied broadly to many different kinds of fruit. Additionally, we have a Stall type, which we can query for details about the different food and produce stalls running in a farmer's market, like their names and location number.

An individual fruit resting on the ground next to an empty market stall.

When thinking about a particular stall, we might ask what kinds of fruit it sells. And alternatively, when considering a particular type of fruit, we might want to know which stall is selling it. We can traverse, or move across, the relationship between these two types to find out more information: we can go from fruit, to the stall that sells it, to other fruits available at the same stall.

Moving from a fruit, to the stall that sells it, then to other fruits sold there.

When an object type includes a field that returns another object type, we can think of this like a point of entry to the second object type's fields. When types are linked in this way, we can use the connecting field like a portal: we can jump between types, gathering the information we need in one bundle, and returning home from our journey with all of it in-hand.

This is the power of the graph at work, which results from building your web of data with GraphQL: types that represent their relationships to one another in this way allow us to build more and more complex queries to drill deeper into data and discover new insights.

Let's see how we can model this relationship in GraphQL.

type Fruit {
id: ID!
name: String!
quantity: Int!
price: Int!
averageWeight: Float
hasEdibleSeeds: Boolean
nutrients: [String]
vendor: Stall
}
type Stall {
id: ID!
name: String!
stallNumber: String!
availableFruits: [Fruit!]!
}

Notice that in this example, each kind of Fruit has a single vendor, which is the field that holds information about the specific Stall that sells the fruit. (It might be possible for a kind of fruit to exist in the database even without a farmer who is currently selling it, which is why the value is nullable!)

We'll see something different on the Stall type. Each stall is given a field called availableFruits, which returns a list of the many different kinds of fruit it sells. Notice that in this case, we expect the availableFruits field to hand back either an empty list, or a list containing actual Fruit items. (The list itself cannot be null, and if it contains items, those items cannot be null.)

Querying for data

The Query type is like the springboard that gets us into the graph, where we can jump from one object type's fields to another and build complex queries that give us exactly the data we're looking for. Right now, fruits is the only way that we can enter the graph and begin querying for data.

type Query {
fruits: [Fruit!]!
}

When we build a query from the root Query type, we start by selecting one of its fields. If this field returns an object type—like fruits returning a list of Fruit—we need to make at least one selection from that object type's subfields. Let's see an example of this in the next section. To illustrate this, consider the Fruit and Stall types below.

type Query {
fruits: [Fruit!]!
}
type Fruit {
id: ID!
name: String!
quantity: Int!
price: Int!
averageWeight: Float
hasEdibleSeeds: Boolean
nutrients: [String]
vendor: Stall
}
type Stall {
id: ID!
name: String!
stallNumber: String!
availableFruits: [Fruit!]!
}

All queries start with the available fields on the Query type. Currently fruits is our only entry point—the only door we can enter through to get some data—and we expect a list of Fruit from it. Because we've now jumped to another object type, Fruit, we need to select at least one of its fields to return in our query.

query GetAllFruit {
fruits {
}
}

Note: The query syntax shown above begins with the query keyword and is followed by a descriptive operation name. Within the first set of curly braces we can define the field on the Query type that gives us access to specific data within our graph.

Try building this query in the embedded Apollo Sandbox below. Starting with the root Query type, build a pathway to get the name, price, and quantity of all available fruit. Want a challenge? See if you can pull the name and stallNumber for each fruit's vendor field!

See the solution!

query GetFruits {
fruits {
name
price
quantity
vendor {
name
stallNumber
}
}
}
Up Next: The Schema  

About

GraphQL.com is maintained by the Apollo team. Our goal is to give developers and technical leaders the tools they need to understand and adopt GraphQL.


GraphQL.com 2023