Odyssey

Full-Stack Quickstart
deprecated

IntroductionBuilding a schemaConnecting to data sourcesWriting query resolversWriting mutation resolversConnecting graphs to Apollo StudioSetting up Apollo ClientFetching data with queriesUpdating data with mutationsManaging local state
2. Building a schema
15m

Creating the blueprint for your graph

New to Apollo? We recommend checking out our introductory Lift-off series, which introduces many of the same concepts as this tutorial with helpful videos and interactive code challenges along the way. Completing the Lift-off series will also grant you the Apollo Graph Developer - Associate Certification, letting you easily share your newfound skills with the world!


If you are familiar with Apollo, this course covers building common useful features like authentication, pagination, and state management.

Every graph uses a schema to define the types of data it includes. For example, the schema for an online bookstore might include the following types:

# A book has a title and an author
type Book {
title: String
author: Author
}
# An author has a name and a list of books
type Author {
name: String
books: [Book]
}

In the steps below, we'll set up a GraphQL server that will enforce our schema's structure, and then we'll define the schema itself.

Set up Apollo Server

A schema is only useful if our graph conforms to the schema's structure. Enforcing a schema's structure is one of the core features of Apollo Server, a production-ready, open-source library that helps you implement your graph's API.

From the start/server directory, let's install Apollo Server (along with our project's other dependencies):

cd start/server && npm install

The two packages you need to get started with Apollo Server are apollo-server and graphql, both of which are installed with the above command.

Now, in the /server directory let's navigate to src/index.js so we can create our server. Paste the code below into the file:

server/src/index.js
// We will cover using dotenv in a later lesson
require("dotenv").config();
const { ApolloServer } = require("apollo-server");
const typeDefs = require("./schema");
const server = new ApolloServer({ typeDefs });

This code imports the ApolloServer class from apollo-server, along with our (currently undefined) schema from src/schema.js. It then creates a new instance of ApolloServer and passes it the imported schema via the typeDefs property.

Task!

Now that Apollo Server is prepared to receive our schema, let's define it.

Define your schema's types

Your GraphQL schema defines what types of data a client can read and write to your graph. Schemas are strongly typed, which unlocks powerful developer tooling.

Your schema's structure should support the actions that your clients will take. Our example app needs to be able to:

  • Fetch a list of all upcoming rocket launches
  • Fetch a specific launch by its ID
  • Log in the user
  • Book a launch for a logged-in user
  • Cancel a previously booked launch for a logged-in user

Let's design our schema to make these actions straightforward.

In server/src/schema.js, import gql from apollo-server and create a variable called typeDefs for your schema:

server/src/schema.js
const { gql } = require("apollo-server");
const typeDefs = gql`
# Your schema will go here
`;
module.exports = typeDefs;

The schema will go inside the gql function (between the backticks). The language we'll use to write the schema is GraphQL's schema definition language (SDL).

Because the schema sits directly between your application clients and your underlying data services, frontend and backend teams should collaborate on its structure. When you develop your own graph, practice schema-first development and agree on a schema before you begin implementing your API. Apollo GraphOS is highly recommended for teams who need to collaborate on GraphQL schema design and implementation.

Object types

Most of the definitions in a GraphQL schema are object types. Each object type you define should represent an object that an application client might need to interact with.

For example, our example app needs to be able to fetch a list of upcoming rocket launches, so we should define a Launch type to support that behavior.

Paste the following inside the backticks of the typeDefs declaration in server/src/schema.js:

server/src/schema.js
type Launch {
id: ID!
site: String
mission: Mission
rocket: Rocket
isBooked: Boolean!
}

The Launch object type has a collection of fields, and each field has a type of its own. A field's type can be either an object type or a scalar type. A scalar type is a primitive (like ID, String, Boolean, Int or Float) that resolves to a single value. In addition to GraphQL's built-in scalar types, you can define custom scalar types.

An exclamation point (!) after a declared field's type means "this field's value can never be null."

In the Launch definition above, Mission and Rocket refer to other object types. Let's add definitions for those, along with the User type (again, all inside the backticks):

server/src/schema.js
type Rocket {
id: ID!
name: String
type: String
}
type User {
id: ID!
email: String!
trips: [Launch]!
token: String
}
type Mission {
name: String
missionPatch(size: PatchSize): String
}
enum PatchSize {
SMALL
LARGE
}

If a declared field's type is in [Square Brackets], it's an array of the specified type. If an array has an exclamation point after it, the array cannot be null, but it can be empty.

Notice above that the missionPatch field of the Mission type takes an argument named size, which is of the enum type PatchSize. When you query for a field that takes an argument, the field's value can vary depending on the provided argument's value. In this case, the value you provide for size will determine which size of the mission's associated patch is returned (the SMALL size or the LARGE size).

The Query type

We've defined the objects that exist in our graph, but clients don't yet have a way to fetch those objects. To resolve that, our schema needs to define queries that clients can execute against the graph.

You define your graph's supported queries as fields of a special type called the Query type. Paste the following into your schema definition:

server/src/schema.js
type Query {
launches: [Launch]!
launch(id: ID!): Launch
me: User
}
Task!

This Query type defines three available queries for clients to execute: launches, launch, and me.

  • The launches query will return an array of all upcoming Launches.
  • The launch query will return a single Launch that corresponds to the id argument provided to the query.
  • The me query will return details for the User that's currently logged in.

The Mutation type

Queries enable clients to fetch data, but not to modify data. To enable clients to modify data, our schema needs to define some mutations.

The Mutation type is a special type that's similar in structure to the Query type. Paste the following into your schema definition:

server/src/schema.js
type Mutation {
bookTrips(launchIds: [ID]!): TripUpdateResponse!
cancelTrip(launchId: ID!): TripUpdateResponse!
login(email: String): User
}

This Mutation type defines three available mutations for clients to execute: bookTrips, cancelTrip, and login.

  • The bookTrips mutation enables a logged-in user to book a trip on one or more Launches (specified by an array of launch IDs).
  • The cancelTrip mutation enables a logged-in user to cancel a trip that they previously booked.
  • The login mutation enables a user to log in by providing their email address.

The bookTrips and cancelTrip mutations return the same object type: a TripUpdateResponse. A mutation's return type is entirely up to you, but we recommend defining special object types specifically for mutation responses.

Add the definition for TripUpdateResponse to your schema:

server/src/schema.js
type TripUpdateResponse {
success: Boolean!
message: String
launches: [Launch]
}

This response type contains a success status, a corresponding message, and an array of any Launches that were modified by the mutation. It's good practice for a mutation to return whatever objects it modifies so the requesting client can update its cache and UI without needing to make a follow-up query.

Task!

Our example app's schema is now complete!

Run your server

Return to server/src/index.js and add the following call to the bottom of the file:

server/src/index.js
server.listen().then(() => {
console.log(`
Server is running!
Listening on port 4000
Explore at https://studio.apollographql.com/sandbox
`);
});

After saving, run npm start to start your server! 🎉 Apollo Server is now available at http://localhost:4000.

Task!

Explore your schema

Now that our server is running, we can introspect its schema types and fields with useful developer tools. Later, we'll also be able to run test queries from those tools.

Introspection is a helpful GraphQL feature that enables you to obtain a server's schema for development purposes. It should be disabled for servers running in production. Apollo Server disables introspection automatically if the NODE_ENV environment variable is set to production.

The Apollo Studio Explorer is a powerful web IDE for exploring your GraphQL schema and building queries against it:

Studio Explorer tab

Apollo Sandbox is a special mode of Apollo Studio that enables you to use Studio features without an Apollo account. Let's try it out!

Visit studio.apollographql.com/sandbox. When Sandbox opens, it automatically attempts to connect to your server running at http://localhost:4000.

To introspect your server's schema in Sandbox, click the Documentation tab on the left side:

Explorer documentation tab

Your schema's root types (Query and Mutation) appear. You can click any type to view its fields and step down into your schema. You'll see that the types and fields match what we provided to our server.

Learn more about the Apollo Studio Explorer

Our server now knows which GraphQL types and operations it supports, but it doesn't know where to obtain the data to respond to those operations. Next, we'll connect our server to two data sources.

Previous
Next
              graph

              A schema-based data model representing how different data elements interconnect and can be accessed.

              GraphQL server

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

              graph

              A schema-based data model representing how different data elements interconnect and can be accessed.

              graph

              A schema-based data model representing how different data elements interconnect and can be accessed.

              Apollo Server

              An open-source library for server-side GraphQL operation handling. It can be used as a monolithic GraphQL server or a subgraph server within a supergraph.

              Apollo Server

              An open-source library for server-side GraphQL operation handling. It can be used as a monolithic GraphQL server or a subgraph server within a supergraph.

              Apollo Server

              An open-source library for server-side GraphQL operation handling. It can be used as a monolithic GraphQL server or a subgraph server within a supergraph.

              GraphQL schema

              A GraphQL schema defines the structure and types of data that can be queried or mutated, serving as a contract between the server and clients.

              launch

              The process of applying a set of updates to a supergraph. Launches are usually triggered by making changes to one of your published subgraph schemas.

              launch

              The process of applying a set of updates to a supergraph. Launches are usually triggered by making changes to one of your published subgraph schemas.

              launch

              The process of applying a set of updates to a supergraph. Launches are usually triggered by making changes to one of your published subgraph schemas.

              launch

              The process of applying a set of updates to a supergraph. Launches are usually triggered by making changes to one of your published subgraph schemas.

              variable

              A placeholder for dynamic values in an operation allowing parameterization and reusability in requests. Variables can be used to fill arguments or passed to directives.

              query GetUser($userId: ID!) {
              user(id: $userId) {
              firstName
              }
              }

              In the query above, userId is a variable. The variable and its type are declared in the operation signature, signified by a $. The type of variable is a non-nullable ID. A variable's type must match the type of any argument it's used for.

              GraphQL

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

              schema definition language

              GraphQL's schema definition language (SDL). The syntax for writing GraphQL schemas. All GraphQL APIs can use SDL to represent their schema, regardless of the API's programming language.

              SDL

              GraphQL's schema definition language (SDL). The syntax for writing GraphQL schemas. All GraphQL APIs can use SDL to represent their schema, regardless of the API's programming language.

              graph

              A schema-based data model representing how different data elements interconnect and can be accessed.

              GraphQL schema

              A GraphQL schema defines the structure and types of data that can be queried or mutated, serving as a contract between the server and clients.

              GraphQL schema

              A GraphQL schema defines the structure and types of data that can be queried or mutated, serving as a contract between the server and clients.

              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!
              }
              launches

              The process of applying a set of updates to a supergraph. Launches are usually triggered by making changes to one of your published subgraph schemas.

              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!
              }
              field

              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
              }
              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!
              }
              scalar

              A "base" type that resolves to a single value. GraphQL includes the following scalar types by default: Int, Float, String, Boolean, and ID.

              GraphQL

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

              scalar

              A "base" type that resolves to a single value. GraphQL includes the following scalar types by default: Int, Float, String, Boolean, and ID.

              field

              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
              }
              object types

              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!
              }
              field

              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
              }
              field

              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.

              field

              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
              }
              argument

              A key-value pair associated with a particular schema field that lets operations pass data to that field's resolver.

              Argument values can be hardcoded as literal values (shown below for clarity) or provided via GraphQL variables (recommended).

              query GetHuman {
              human(id: "200") {
              name
              height(unit: "meters")
              }
              }
              graph

              A schema-based data model representing how different data elements interconnect and can be accessed.

              graph

              A schema-based data model representing how different data elements interconnect and can be accessed.

              graph

              A schema-based data model representing how different data elements interconnect and can be accessed.

              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.

              argument

              A key-value pair associated with a particular schema field that lets operations pass data to that field's resolver.

              Argument values can be hardcoded as literal values (shown below for clarity) or provided via GraphQL variables (recommended).

              query GetHuman {
              human(id: "200") {
              name
              height(unit: "meters")
              }
              }
              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.

              mutations

              A GraphQL operation that modifies data on the server. It allows clients to perform create, update, or delete operations, altering the underlying data.

              mutation

              A GraphQL operation that modifies data on the server. It allows clients to perform create, update, or delete operations, altering the underlying data.

              launch

              The process of applying a set of updates to a supergraph. Launches are usually triggered by making changes to one of your published subgraph schemas.

              mutation

              A GraphQL operation that modifies data on the server. It allows clients to perform create, update, or delete operations, altering the underlying data.

              mutation

              A GraphQL operation that modifies data on the server. It allows clients to perform create, update, or delete operations, altering the underlying data.

              mutations

              A GraphQL operation that modifies data on the server. It allows clients to perform create, update, or delete operations, altering the underlying data.

              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!
              }
              mutation

              A GraphQL operation that modifies data on the server. It allows clients to perform create, update, or delete operations, altering the underlying data.

              object types

              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!
              }
              mutation

              A GraphQL operation that modifies data on the server. It allows clients to perform create, update, or delete operations, altering the underlying data.

              query

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

              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
              }
              Introspection

              A special GraphQL query that enables clients and tools to fetch a GraphQL server's complete schema. Introspection should be disabled in production.

              GraphQL

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

              Apollo Server

              An open-source library for server-side GraphQL operation handling. It can be used as a monolithic GraphQL server or a subgraph server within a supergraph.

              introspection

              A special GraphQL query that enables clients and tools to fetch a GraphQL server's complete schema. Introspection should be disabled in production.

              GraphQL schema

              A GraphQL schema defines the structure and types of data that can be queried or mutated, serving as a contract between the server and clients.

              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

              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.

              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