Files
apollo/docs/source/tutorial/schema.md
2018-11-07 09:26:27 -08:00

9.1 KiB

title, description
title description
1. Build a schema Start here for the Apollo fullstack tutorial

Set up Apollo Server

Apollo Server is a library that helps you build a production-ready graph API over your data. It can connect to any data source, including REST APIs and databases, and it seamlessly integrates with Apollo developer tooling.

We need to install two packages when setting up Apollo Server:

npm install apollo-server graphql --save
  • apollo-server: This is the Apollo Server library.
  • graphql: This package is the JavaScript reference implementation for GraphQL. It's needed for Apollo Server to function as intended.

Create an src directory and add an index.js file.

Now, import ApolloServer and gql from the apollo-server library.

src/index.js

const { ApolloServer, gql } = require('apollo-server');

In the code above, we imported the ApolloServer class and gql tag from the apollo-server package.

  • ApolloServer: The ApolloServer class instantiates and starts a new GraphQL server.
  • gql: The gql tag is a JavaScript template literal tag that enables syntax highlighting for our schema.

Write your graph's schema

Every GraphQL server runs a schema at its core. A schema defines types and their relationships. The specifications of the types of queries that can be run against a GraphQL server are defined in a schema. Let's design the schema for our app.

GraphQL schemas are at their best when they are designed around the needs of client applications. In fact, this concept is called Schema First Development, an approach for building applications with GraphQL that involves the frontend and backend teams agreeing on a schema first, which serves as a contract between the UI and the backend before any API development commences.

In our app, we need to provide the following features:

  • Fetch all upcoming rocket launches.
  • Fetch a specific launch.
  • A user should be able to login to be authorized to book and cancel launch trips.
  • A user should be able to book launch trips.
  • A user should be able to cancel launch trips.

We'll use these features to derive the form of queries and mutations our schema needs.

  • First feature: An example of a query for fetching all upcoming rocket launches would look like:
{
  launches {
    id
    year
    passengers
  }
}
  • Second feature: An example of a query for a specific launch would look like:
{
  launch(id: 2) {
    id
    year
  }
}

An id is passed to the launch query and the details of the 2nd launch is returned. It could be the first or third or any number specified in the query.

From the third feature, we start to deal with users. A query for a signed in user come to mind here and that would look like:

{
  user {
    id
    email
  }
}

Still focusing on the third feature, a user needing to log in seems more than just a query. In most cases, data changes occur such as a new user profile been created because the user doesn't exist. In this case, we're dealing with a mutation, which is a special type of query that modifies data.

mutation {
  login(email: "johndoe@gmail.com") {
    id
  }
}

The fourth feature signifies creation of new data. An example of a mutation would look like:

mutation bookTrip {
  bookTrip(launchId: 3) {
    id
  }
}

The fifth feature signifies destruction of data. An example of a mutation for this case would look like:

mutation cancelTrip {
  cancelTrip(launchId: 3) {
    id
  }
}

Let's make some schema types now based on these queries and mutations, starting with the Query and Mutation types which are the entry points into a schema.

type Query {
  launches: [Launch]!
  launch(id: ID!): Launch
  me: User
}

Launch is a GraphQL Object Type that has some fields. Once your query requires more than one data attribute to be returned, group them into an object type like the type below:

type Launch {
  id: ID!
  cursor: String
  year: String!
  date: String!
  mission: Mission!
  rocket: Rocket!
  launchSuccess: Boolean
  passengers: [User]!
}

In the Launch type, we have fields with scalar types and object types. GraphQL has built-in scalar types, Int, String, Boolean, ID which represents the leaves of an operation while object types are user-defined GraphQL types. The object types here are Mission, Rocket, and User. We need to define the fields for these object types.

Note: The cursor field in the Launch type is there for pagination purposes. We'll cover that later in the tutorial.

type Mission  {
  name: String!
  missionPatch: String
}

This represents a mission that requires a name and missionPatch details.

type Rocket {
  id: ID!
  name: String!
  type: String!
}

This represents a rocket for a launch. An ID, name, and type fields are needed to identify a rocket.

type User {
  id: ID!
  email: String!
  trips: [Launch]!
}

The id and email are fields for identifying a user. The trips field is necessary for providing easy access to the number of trips a user has booked.

Let's now deal with the Mutation type.

The Mutation type defines the entry points into Apollo server for modifying server data. For the Mutation type, based on our sample mutation queries, we can safely conclude that it should look like:

type Mutation {
  bookTrip(launchId: ID!): Boolean!
  cancelTrip(launchId: ID!): Boolean!
  login(email: String): String
}

All the fields in the Mutation type returns a scalar type. The bookTrip field for booking a trip, cancelTrip field for cancelling a trip and the login field for authenticating a user.

Now, put everything together in a src/schema.js file.

src/schema.js

const { gql } = require('apollo-server');

const typeDefs = gql`
  type Query {
    launches(
      """
      the number of results to show. Must be >= 1
      """
      pageSize: Int
      """
      cursor - if you add a cursor id here, it will only return results _after_ this cursor
      """
      after: String
    ): [Launch]!
    launch(id: ID!): Launch
    me: User
  }
  type Mutation {
    bookTrip(launchId: ID!): Boolean!
    cancelTrip(launchId: ID!): Boolean!
    login(email: String): String
  }
  type Launch {
    id: ID!
    cursor: String
    year: String!
    date: String!
    mission: Mission!
    rocket: Rocket!
    launchSuccess: Boolean
    passengers: [User]!
  }
  type Rocket {
    id: ID!
    name: String!
    type: String!
  }
  type User {
    id: ID!
    email: String!
    trips: [Launch]!
  }
  type Mission {
    name: String!
    missionPatch: String
  }
`;

module.exports = typeDefs;

In the code above, the launches field of the Query type takes in 2 arguments. Don't worry about those arguments for now, they are for pagination.

Run your server

Now that we have scoped out our app's schema, let's run the server. Copy the code below and paste in the index.js file.

src/index.js

...
const typeDefs = require('./schema');

const server = new ApolloServer({ typeDefs });

server.listen().then(({ url }) => {
  console.log(`🚀 Server ready at ${url}`);
});

The schema typeDefs is passed to the ApolloServer constructor, then ApolloServer's listen() method is invoked to run the server.

On your terminal, run:

npm start

The start script in the package.json file should look like:

...
"scripts": {
  "start": "nodemon src/index.js"
},
...

Apollo Server will now be available on port 4000. By default, it supports GraphQL Playground. The Playground is an interactive, in-browser GraphQL IDE for testing your queries. Apollo Server automatically serves the GraphQL Playground GUI to web browsers in development. When NODE_ENV is set to production, GraphQL Playground is disabled as a production best-practice.

The GraphQL Playground provides the ability to introspect your schemas. Check out the right hand side of the playground and click on the schema button.

Introspection is a technique used to provide detailed information about a GraphQL API's schema. If we didn't design the GraphQL type system, introspection allows us to query GraphQL for the schema type via the __schema field that's accessible on a query's root type. In GraphQL Playground, just run:

{
  __schema {
    types {
      name
    }
  }
}

and all the types will be returned. The types prefixed with a double underscore are part of the introspection system.

![Schema button](../images/schematab.png)

The schema types are shown like you have below:

![Introspection](../images/introspection.png)

You can quickly have access to the documentation of a GraphQL API via the schema button.

![More details on a Schema Type](../images/moredetailsonatype.png)

That's all for running Apollo Server for now. Let's move on to the next part of our tutorial.