Learn more about GraphQL, including its object types that represent the kind of objects we can fetch from our service and what fields they have.
In some of our past articles, we’ve discussed how to create a GraphQL API in Node.js and how to get started with React and GraphQL. In today’s article, we’ll spend a little time discussing some core concepts of GraphQL APIs—the GraphQL schema and its object types.
As a refresher, GraphQL is a query language for making requests to APIs. With GraphQL, the client tells the server exactly what it needs and the server responds with the data that has been requested. For example, assume we could query the title
and price
fields on a listing
object as follows:
{
listing {
title
price
}
}
This could return data like the following:
{
"data": {
"listing": {
"title": "Beachfront condo...",
"price": 50
}
}
}
If this were an API we’re working with, how could we know that the listing
object has the title
and price
properties? Does the listing
object have more properties we can select? This is where the GraphQL Schema comes in.
Every GraphQL API has a schema that completely describes all the possible data we can request and is the blueprint of a GraphQL API. When a request comes in, it is validated against this schema and processed accordingly.
At the core of every GraphQL schema are the object types, which represent the kind of objects we can fetch from our service and what fields they have.
For our previous example above, we had a listing
object type, which contains title
and price
fields. This is how we might define the listing
object in a GraphQL schema:
type Listing {
id: ID!
title: String!
address: String!
price: Int!
}
We’re defining a GraphQL object type named Listing
. Within this object, there are several properties:
id
property that corresponds to a GraphQL ID
.title
and address
properties, both of which are represented as GraphQL String
types.price
property that is characterized as a GraphQL Int
.This syntax is known as the GraphQL Schema Language.
In a GraphQL schema, it’s possible to establish connections between various object types. For instance, the Listing
object type might feature a tenant
field which itself can be represented as another object type, such as the User
object type.
type Listing {
"..."
tenant: User!
}
Conversely, the User
object type could have a relationship wherein it contains a listing
field, representative of the Listing
object type.
type User {
"..."
listing: Listing!
}
The above illustrates one-to-one connections, signifying that a single listing corresponds to a specific user and vice versa. Additionally, one-to-many connections can be established. An example is the User
object type having a listings
field, which yields an array of listings.
type User {
"..."
listings: [Listing!]!
}
In a GraphQL schema, there are two distinct object types named Query
and Mutation
. A GraphQL schema must contain a Query
type, whereas the inclusion of a Mutation
type is optional.
Both Query
and Mutation
act as the primary access points for all GraphQL interactions. The Query
type outlines all the available pathways to retrieve data, while the Mutation
type specifies the entryways to modify data.
For instance, we might design a Query
type to fetch a field termed listings
. Similarly, a Mutation
type could incorporate a deleteListing
field that expects an id
parameter.
type Query {
listings: [Listing!]!
}
type Mutation {
deleteListing(id: ID!): Listing!
}
The listings
query and the deleteListing
above are seen to both return variations of the Listing
object type.
In GraphQL, when defining the structure of our data, every attribute of an object will eventually boil down to a fundamental value. This foundational value is represented by what we term scalar types. These are elementary data types that are indivisible—they don’t break down into more specific types or have nested attributes.
By default, GraphQL provides a collection of predefined scalar types:
This represents a basic binary choice and can either be true
or false
.
Denotes a signed 32‐bit whole number. This can range from small to relatively large integers which accommodates most common numeric use cases.
Corresponds to a signed double-precision floating-point figure. In simpler terms, it represents decimal numbers which can be particularly useful when dealing with measurements, monetary values or other precise calculations.
Reflects a sequence of characters, encoded in UTF‐8 format. It’s versatile and can hold anything from names to descriptions or even entire articles.
This is a special scalar type, primarily designed to signify unique identifiers for objects. While the underlying idea is that they aren’t primarily meant for human interpretation, in practice, they’re serialized similarly to String
. Often, this type aids in fetching specific data entries or ensuring data consistency.
Enumeration types, often referred to as enums, are unique scalar types confined to a predefined set of values. For instance, if we were to add a listingType
attribute to our previously mentioned Listing
object and we want to ensure that this attribute can only assume specific values, we would use an enum
type:
enum ListingType {
HOUSE
APARTMENT
}
While objects, scalars and enums represent the core types, we can apply additional modifiers to affect their behavior. Specifically, the use of square brackets in the GraphQL Schema Language allow fields to be defined as lists. Consider this example where we introduce a bookings
attribute that returns a list comprising the Booking
object type:
type Listing {
"..."
bookings: [Booking]
}
Observe the distinction between the [Booking]
notation and [Booking!]!
. The presence of !
is a mechanism in GraphQL to designate fields as non-nullable. This implies that the field will always resolve to the specified type and will never return a null
value.
type Listing {
"users will always be a non-empty list containing User type elements"
users: [User!]!
"bookings might be null, or individual elements in the bookings list might be null"
bookings: [Booking]
}
This nuanced usage provides a lot of flexibility and precision when shaping the data structure in GraphQL.
In GraphQL, fields operate similarly to functions in traditional programming languages, where they can accept arguments and return values based on those inputs.
Consider a scenario where we want to supply multiple parameters to a createListing
GraphQL mutation field to generate a desired outcome (for instance, to create a listing). Just like how we can provide multiple arguments to a function in languages like JavaScript, we can do the same with a GraphQL field.
type Mutation {
createListing(
id: ID!
title: String!
address: String!
price: Int!
): Listing!
}
A widely adopted practice in situations like this is to use input object types. In GraphQL’s schema notation, these input types resemble standard object definitions but are prefixed with the input
keyword, distinguishing them from regular object types.
For instance, we can define a createListing
mutation that mandates a non-nullable argument of CreateListingInput
input type.
input CreateListingInput {
id: ID!
title: String!
address: String!
price: Int!
}
type Mutation {
createListing(input: CreateListingInput!): Listing!
}
This structure not only makes the schema more readable but also simplifies the process of extending or modifying input parameters in the future.
GraphQL is a versatile query language that offers great flexibility in requesting and manipulating data. Its schema-based structure ensures a clear contract between clients and servers, enabling the client to have precise control over the data they wish to access. Object types form the cornerstone of this schema facilitating the definition of various data shapes and their interactions.
In some upcoming articles, we’ll discuss and cover some other important GraphQL core concepts such as resolver functions, pagination and caching.
Hassan is a senior frontend engineer and has helped build large production applications at-scale at organizations like Doordash, Instacart and Shopify. Hassan is also a published author and course instructor where he’s helped thousands of students learn in-depth frontend engineering skills like React, Vue, TypeScript, and GraphQL.