4. Building our schema
10m

✏️ 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 .
  • The graphql package provides the core logic for parsing and validating 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 strings like the schema definition we're about to write.

This converts strings into the format that Apollo libraries expect when working with 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 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 , 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 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 , 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 :

  • 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 what to retrieve when we 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 :

type Query {
# Fields go here
}

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

For now, we're only interested in fetching the track list for our homepage. Let's name that specific tracksForHome to make it as descriptive as possible. We want this 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 .

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!.

Loading...
Loading progress
Previous

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.