✏️ 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 thegql
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: Track
s and Author
s.
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 typeID!
title
of typeString!
author
of typeAuthor!
(we'll define theAuthor
type when we're done withTrack
)thumbnail
of typeString
(a URL to the image for the track's card)length
of typeInt
modulesCount
of typeInt
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: Stringlength: IntmodulesCount: 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.
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 typeID!
name
of typeString!
photo
of typeString
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 Track
s. 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:
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;
Query
type?Now that our base schema is ready, we can start working on our GraphQL server.
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!
.
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.