Odyssey

Lift-off I: Basics

Feature overview and setupFeature data requirementsSchema definition language (SDL)Building our schemaApollo ServerApollo ExplorerThe frontend appApollo Client setupDefining a queryThe useQuery hook
4. Building our schema
5m

✏️ Let's define that schema

With our IDE opened to our freshly checked out Catstronauts project, let's navigate to the server/src/ directory. In there, we'll create a new schema.js file.

To get started with our schema, we'll need a couple packages first: @apollo/server, graphql and graphql-tag.

  • The @apollo/server package provides a full-fledged, spec-compliant GraphQL server.
  • The graphql package provides the core logic for parsing and validating GraphQL queries.
  • The graphql-tag package provides the gql template literal that we'll use in a moment.

From the server/ directory, run the following:

npm install @apollo/server graphql graphql-tag

Now in schema.js, let's obtain the gql template literal from graphql-tag:

const gql = require("graphql-tag");

What is this gql thing we're importing? It's a tagged template literal, used for wrapping GraphQL strings like the schema definition we're about to write.

This converts GraphQL strings into the format that Apollo libraries expect when working with operations and schemas, and it also enables syntax highlighting.

Next, let's declare a typeDefs (short for "type definitions") constant, assigning the gql template where our definitions will go. While we're at it, let's export typeDefs now, because we'll need it for our server file later on.

const typeDefs = gql`
# Schema definitions go here
`;
module.exports = typeDefs;

Note the use of backticks (`) with the gql tag, not to be confused with single quotes (').

Great, we're ready to define our types. Referring back to our mockup, we identified that we need the following data for each learning track:

  • Title
  • Thumbnail
  • Length
  • ModulesCount
  • Author name
  • Author picture

How do we organize this data into types?

Well, we could create a single type named Track, shove all those fields into it, and call it a day. But would that make sense from a business domain point of view? Not really. For starters, a single author might create multiple tracks, and that author's information would be needlessly duplicated across multiple locations. Instead, we need to think in terms of standalone entities. We'll start with two: Tracks and Authors.

The Track type

We'll start with the type Track that represents a particular learning track. Let's define the type and add a description right away:

"A track is a group of Modules that teaches about a specific topic"
type Track {
# Fields go here
}

Now for the track's fields, we'll have:

  • id of type ID!
  • title of type String!
  • author of type Author! (we'll define the Author type when we're done with Track)
  • thumbnail of type String (a URL to the image for the track's card)
  • length of type Int
  • modulesCount of type Int

Here's our complete Track type:

"A track is a group of Modules that teaches about a specific topic"
type Track {
id: ID!
title: String!
author: Author!
thumbnail: String
length: Int
modulesCount: Int
}

How do we determine which of these fields should be allowed to be null? One approach is to make the schema reflect our "business" domain rules. In our case, a track could exist without a thumbnail for instance, but a track without a title or author doesn't make any sense from our "business" point of view.

What does an exclamation mark after a field's type indicate?

Add some nice descriptions for each of these fields, then let's move on to the Author type.

The Author type

"Author of a complete Track or a Module"
type Author {
# Fields go here
}

The Author type contains only three fields:

  • id of type ID!
  • name of type String!
  • photo of type String

Here's the complete type:

"Author of a complete Track or a Module"
type Author {
id: ID!
name: String!
photo: String
}

Excellent, our first feature is now fully represented in our schema. These are the data types we'll be able to retrieve.

We're still missing one piece though: how to tell the GraphQL server what to retrieve when we query it. Remember, we don't have multiple specific endpoints to target different types like a REST API does. Instead, we define a special Query type.

The Query type

The Query type is defined like any other object type:

type Query {
# Fields go here
}

The fields of this type are entry points into the rest of our schema. These are the top-level fields that our client can query for.

For now, we're only interested in fetching the track list for our homepage. Let's name that specific query tracksForHome to make it as descriptive as possible. We want this query to return a non-null list of non-null Tracks. We'll also add a nice description:

type Query {
"Get tracks array for homepage grid"
tracksForHome: [Track!]!
}

Our schema is now fully defined to support our first feature! Here's how the whole schema.js file looks:

server/src/schema.js
const gql = require("graphql-tag");
const typeDefs = gql`
type Query {
"Get tracks array for homepage grid"
tracksForHome: [Track!]!
}
"A track is a group of Modules that teaches about a specific topic"
type Track {
id: ID!
"The track's title"
title: String!
"The track's main author"
author: Author!
"The track's main illustration to display in track card or track page detail"
thumbnail: String
"The track's approximate length to complete, in minutes"
length: Int
"The number of modules this track contains"
modulesCount: Int
}
"Author of a complete Track"
type Author {
id: ID!
"Author's first and last name"
name: String!
"Author's profile picture url"
photo: String
}
`;
module.exports = typeDefs;
Which of these are always true about the Query type?

Now that our base schema is ready, we can start working on our GraphQL server.

Code Challenge!

Create a full schema with: a type Query containing a field spaceCats to fetch a List of SpaceCat. A type SpaceCat with its subfields: id of type ID!, name of type String!, age of type Int and missions of type List of Mission. Finally define the Mission type with its subfields: id of type ID!, name of type String!, and description of type String!.

Previous
Next

Share your questions and comments about this lesson

Your feedback helps us improve! If you're stuck or confused, let us know and we'll help you out. All comments are public and must follow the Apollo Code of Conduct. Note that comments that have been resolved or addressed may be removed.

You'll need a GitHub account to post below. Don't have one? Post in our Odyssey forum instead.

              GraphQL server

              A server that contains a GraphQL schema and can resolve client-requested operations that are executed against that schema.

              GraphQL

              An open-source query language and specification for APIs that enables clients to request specific data, promoting efficiency and flexibility in data retrieval.

              GraphQL

              An open-source query language and specification for APIs that enables clients to request specific data, promoting efficiency and flexibility in data retrieval.

              GraphQL

              An open-source query language and specification for APIs that enables clients to request specific data, promoting efficiency and flexibility in data retrieval.

              operations

              A single query, mutation, or subscription that clients send to a GraphQL server to request or manipulate data.

              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              entities

              An object type of Apollo Federation that can be fetched with one or more unique keys. Can resolve its fields from multiple data sources in a federated graph.

              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              GraphQL server

              A server that contains a GraphQL schema and can resolve client-requested operations that are executed against that schema.

              query

              A request for specific data from a GraphQL server. Clients define the structure of the response, enabling precise and efficient data retrieval.

              object type

              A type in a GraphQL schema that has one or more fields. User is an object type in the following example:

              type User {
              name: String!
              }
              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              fields

              A unit of data that belongs to a type in a schema. Every GraphQL query requests one or more fields.

              type Author {
              # id, firstName, and lastName are all fields of the Author type
              id: Int!
              firstName: String
              lastName: String
              }
              query

              A request for specific data from a GraphQL server. Clients define the structure of the response, enabling precise and efficient data retrieval.

              query

              A request for specific data from a GraphQL server. Clients define the structure of the response, enabling precise and efficient data retrieval.

              query

              A request for specific data from a GraphQL server. Clients define the structure of the response, enabling precise and efficient data retrieval.

              GraphQL server

              A server that contains a GraphQL schema and can resolve client-requested operations that are executed against that schema.

              NEW COURSE ALERT

              Introducing Apollo Connectors

              Connectors are the new and easy way to get started with GraphQL, using existing REST APIs.

              Say goodbye to GraphQL servers and resolvers—now, everything happens in the schema!

              Take the course