Introduction to GraphQL

Scott Moss & FrontEnd Masters

Intro and tools

Famililar tools

  • Node.js for runtime
  • MongoDB for our Database
  • Mongoose for interacting with our Database
  • Jest for testing everything

GraphQL?

  • Open source and created by Facebook
  • Gives clients the power to describe exactly what data they want
  • Strongly typed (think typescript for your data)
  • Enables excellent developer tooling and experiences
  • Can sit in front of any existing API because its just a query language
  • Ecosystem is fantastic

tldr; Strongly typed query language and runtime for your data

GraphQL vs REST

  • GraphQL only has one URL. It does not need a resource url + verb combo. The request details are in a POST body (sometimes GET)
  • In REST, the shape and size of the data resource is determined by the server, with Graphql its determined by the query (request)
  • In REST, you have to male multiple API calls to retrieve relational data, with GraphQL you can start with entry resource and traverse all the connections in one request
  • In REST, the shape of the response is determined by whom ever created the server, in GraphQL the shap is determined by the query
  • In REST, a single request will execute on controller on the server, in GraphQL a request might execute MANY resolvers on the server
  • many more....

tldr; Different but sneakily similar

Exercise 1

  1. ✅ checkout to lesson-1 branch
  2. ✅ create a type on the schema string
  3. ✅ create a query field for your type in the schema string
  4. ✅ create a resolver for your query
  5. ✅ add your resolver to the server
  6. ✅ start the server and explore your API using GraphQL playground

In this lesson you'll be creating a simple GraphQL server using Apollo Server. 

Creating Schemas

We already have a DB Schema

  • DB Schema is for keeping data consistent when entering our DB
  • GraphQL schema is for defining what resources are available for querying, how they relate, and how you can query them
  • Both schemas can be the same, or not. DB schema is a good starting point for your GraphQL schema
  • GraphQL schema sits in front of your DB queries, it validates incoming request queries.
  • Some GraphQL tools create GraphQL APIs based off of your DB Schemas, and the other way around

tldr; GraphQL schema strictly defines what resources, how they relate, and how a client and consume them

Creating schemas with SDL

  • Schema Definition Language (SDL)
  • Instead of using functions to create a schema, use a verbose, string based syntax for your schemas. Later you can transform that syntax into many other representations if needed
  • Much easier to read
  • Can be composable
  • Supported by all tools 

tldr; Many ways to create schemas, SDL is the best

Scalar and Object types

  • Scalar types are built in primitives
    • String
    • Int
    • Float
    • Boolean
    • ID
  • Object types are custom shapes with fields that either scalar types or other object types
  • Object type fields also describe any arguments and or validations
  • Types are the target or all requests
  • Schema cheatsheet

tldr; Describe resources that will be used in queries and mutations

Query and Mutation types

  • Query type describes the different queries your API is capable of.
  • A query operation is just a name, with possible arguments that eventually returns a type
  • A mutation is the exact same thing, but with the intent of mutating the DB vs just reading
  • Queries and Mutations are what will be available to clients interacting with your API, think of them as your routes

tldr; CRUD on your GraphQL API

Exercise 2

  1. ✅ checkou to lesson-2 branch
  2. create Type for product
  3. create inputs for product
  4. create queries for product
  5. create mutations for product
  6. ensure all tests pass by running test command

This exercise will have you creating a GraphQL Schema based on the the mongoose models already created

Resolvers

What are resolvers?

  • Resolvers are like controllers in a REST API. They are responsible for retrieving data
  • Every query and mutation your schema has, must have a resolver that returns the specified type
  • Types and fields on types often have resolvers as well
  • Incoming query dictates what resolvers run and in what order

tldr; Like controllers, but instead resolve types all the way down

Creating resolvers

  • Resolvers take a few args
    • starting object (what the parent resolver returned or starting value from server)
    • args (any arguments from the incoming request)
    • context (shared context obj across all resolvers, like the req object in express)
    • info (advanced AST of the incoming request)
  • Any errors is caught and immediately send back to the client
  • Can of course be async
  • Can do whatever you like as long as they return their types

tldr; Return the same shape as described in the schema, or delegate to another resolver

Exercise 3

  1. ✅ checkout to lesson-3 branch
  2. ✅ create resolvers for product queries
  3. ✅ create resolvers for product mutations
  4. ✅ create resolvers for prouduct createdBy field
  5. ✅ ensure all tests pass by running test command

In this exercise, you'll be creating resolvers for the Queries and Mutations on the product type. You'll be using Mongoose models to perform CRUD in your resolvers.

Interfaces, Unions, and Fragments

Interfaces

  • Some types are very similar with the exception of a few fields. You can use an interface as a base type and have other types implement that interace. Just like a language type system
  • You then have to use fragments in your request query to conditonally ask for type specific fields

tldr; Inheritable types for your schema

Unions

  • Some times you want a query to return a possibility of more than just one type. Unions allow you to create a type that is composed of many types where any of them may be fulfilled
  • Great for search queries

tldr; A combination type that can be one of many different types that may not relate to each other

Resolving Interfaces and Unions

  • You have to create a special resolver that accepts the previously resolved data and return the GraphQL type name of the intended Type.

tldr; Have to create a resolver that tells graphql what type to resolve to

Exercise 4

  1. ✅ checkout to lesson-4 branch
  2. ✅ change product type to an interface
  3. ✅ create Bike type that implements product
  4. ✅ create GamingPc type that implements product
  5. ✅ create Drone type that implements product
  6. ✅ create resolver for product interface that resolves the type
  7. ✅ ensure all tests pass by running test command

Now that you know about schemas and resolvers, we need to make some changes. Our product model in mongoose is split between 3 different product types. We need to make the product type an interface and then create types for each possible type in our mongoose model. Don't forget to create  resolver to resolve the type.

Auth

Many ways to auth

  • Lock down the entire API by checking auth outside of GraphQL or when creating the context object
  • Handle auth in the resolvers (just make sure you enhance the context object with all that you need)
  • Use custom directives in your SDL 🔥
  • Use resolver middleware (in some frameworks)
  • choose wisely

tldr; There is not wrong way

Exercise 5

  1. ✅ checkout to lesson-5 branch
  2. ✅ authenticate the request and add use to context
  3. ✅ block all product queries and mutations if no user
  4. ✅ block all product mutations if not an admin role
  5. ✅ ensure all tests pass by running test command

There are many many ways to authenticate with GraphQL. Our API is a public API, so we'll use API keys. Some queries need authentication, and some queries also need the correct role. Authenticate the request and update the product resolvers!

Intro to GraphQL

By Scott Moss

Intro to GraphQL

  • 4,772