Skip to content

Latest commit

 

History

History
556 lines (377 loc) · 12.3 KB

Lesson-3.md

File metadata and controls

556 lines (377 loc) · 12.3 KB

ACS 4390 - GraphQL + Express

Learning Outcomes

By the end of today's lesson, you should be able to...

  1. Build a GraphQL API over a Public API
  2. Use GraphQL and Express
  3. Define Resolvers for types
  4. Use Resolvers to handle queries

Review

Pop Quiz!

Write a GraphQL schema for these types

Monster Battle

  • Write the following in the GraphQL Schema language
    • Kaiju type, this is a giant monster like Godzilla
    • City type, what fields does a city have?
    • Battle type should include two monsters and a city
    • Query type that returns a battle
  • Write the following in the graphql Query language!
    • Write a query that summons Godzilla and Mothra to battle in Tokyo
    • print the monsters name, and power
    • print a city's name and population

Card Game

  • Use the GraphQL Schema language
    • Card type for a playing card
    • Hand type for a hand of cards
    • Query type that returns a hand of cards
  • Write a query in the graphql query language
    • Write a query that gets a hand of cards
    • print the value and suit of each card

Users and Images

  • Use the GraphQL Schema language to generate these types
    • Image type should know the URL and size of an image
    • Location type needs latitude and longitude
    • Image type should include a location
    • User Type
    • User type needs a list of images
    • Query type that returns a User
  • Use the graphQL Query language to query:
    • Write a query that gets users images
    • Display the user name
    • Display the image URL

Overview 🌏

Today you will make a GraphQL service for a public API.

Why? This will give you a chance to practice the ideas from the previous class in a different context.

GraphQL 😎 and Express 🚂

GraphQL is a specification and a language. It's not a framework or library of prewritten code.

This means we are free to write libraries or frameworks that implement the GraphQL spec.

You'll find GraphQL libraries written for the most popular frameworks. Today you will use Express.js 🚂 and GraphQL 😎.

What's needed 🤔

  • express-graphql npm package
  • graphql npm package
  • express npm package

How do you set this up?

  • import the npm packages
    • express, graphql, express-graphql
  • define your schema
  • define your resolvers
  • define a route to act as the GraphQL endpoint
    • Use graphqlHTTP to handle requests at this route
    • configure graphqlHTTP with your schema and resolvers

Challenge

The challenge today is to build a GraphQL front end for a public API.

Think of this as an interview question practice.

For this example, you'll use https://openweathermap.org.

Q: Why are using OpenWeatherMap.org?
A: It's free and easy. It's a good choice for a 2-hour assignment.

Challenge 1 - Setup Express and GraphQL

Follow these steps to set up Express and GraphQL.

  1. Create a new folder
  2. Initialize a new npm project: npm init -y
  3. Install dependencies: npm install --save express express-graphql graphql
  4. Create a new file: server.js
  5. Add "start": "nodemon server.js" to package.json

Important! Be sure to include a .gitignore.

https://www.toptal.com/developers/gitignore/api/node

In server.js import your dependancies:

// Import dependancies
const express = require('express')
const { graphqlHTTP } = require('express-graphql')
const { buildSchema } = require('graphql')

Start your schema:

const schema = buildSchema(`
# schema here
type Test {
  message: String!
}
`)

Set up your resolver

const root = {
  // resolvers here
}

Create an express app

// Create an express app
const app = express()

Define a route/endpoint for your GraphQL API

// Define a route for GraphQL
app.use('/graphql', graphqlHTTP({
  schema,
  rootValue: root,
  graphiql: true
}))

Be sure to set graphiql to true since this will enable the graphiql browser that you will be using.

Start your App

// Start this app
const port = 4000
app.listen(port, () => {
  console.log('Running on port:'+port)
})

Start your GraphQL server:

npm start

Open graphiql:

http://localhost:4000/graphql

Challenge 2 - Get your API Key

Go to https://openweathermap.org

  1. Create an account at openweathermap.org.
  2. After you make an account, click on your username, then on My API Keys. Enter an API key name, then click the Generate button to create your API key!

Quick Side Note for .env files

Having a .env file allows us to store our secrets (like an API Key) without it being exposed to the public on GitHub. Let's create that now so we can use our API Key in our project without exposing it!

Install dotenv:

npm install dotenv
  1. In the folder containing the sample project, run touch .env in the terminal
  2. Open the .env file, and place the following in it, replacing MY_API_KEY with your actual API Key:
OPENWEATHERMAP_API_KEY=__MY_API_KEY__

Save it when you're done. Alright, now we're ready to continue!

Be sure to initialize dotenv in server.js:

// require dotenv and call cofig
require('dotenv').config()

Use your API key in your code with:

const apikey = process.env.OPENWEATHERMAP_API_KEY

Challenge 3 - Define Schema

Define your schema

type Weather {
  temperature: Float!
  description: String!
}

type Query {
  getWeather(zip: Int!): Weather!
}

Define a weather type and a query type to get the weather.

Challenge 4 - Import node-fetch

Import node-fetch to make network calls. You can also use Axios or another library of your choice. You need something to make network requests.

npm install node-fetch@2

Add import to server.js

const fetch = require('node-fetch')

Challenge 5 - Define your Resolver

Define your resolver:

const root = {
  getWeather: async ({ zip }) => {
    const apikey = process.env.OPENWEATHERMAP_API_KEY
    const url = `https://api.openweathermap.org/data/2.5/weather?zip=${zip}&appid=${apikey}`
    const res = await fetch(url)
    const json = await res.json()
    const temperature = json.main.temp
    const description = json.weather[0].description
    return { temperature, description }
  }
}

I used fetch here you substitute your HTTP client of choice here

Challenge 6 - Test your work in GraphiQL

Try out a query and solve any errors that might pop up.

{
  getWeather(zip: 94010) {
    temperature
    description
  }
}

Challenge 7 - Add units

The weather API supports a unit of standard, metric, or imperial. Currently, you should be getting the weather in Kelvin (standard) this is hard to understand better to allow a request to include the unit.

Add an enum for the type to your schema.

enum Units {
  standard
  metric
  imperial
}

Use the unit in your getWeather query.

type Query {
  getWeather(zip: Int!, units: Units): Weather!
}

Handle the unit in your resolver.

const root = {
  getWeather: async ({ zip, units = 'imperial' }) => {
    ...
    const url = `https://api.openweathermap.org/data/2.5/weather?zip=${zip}&appid=${apikey}&units=${units}`
    ...
  }
}

Be sure add units to the query string!

Test your work! Write a query:

{
  getWeather(zip: 94122, units: metric) {
    temperature
    description
  }
}

Notice that the enum value is NOT input as a string! Graphiql will code hint valid values! Go GraphQL introspection FTW!

Challenge 8 - Expand the API

If you followed all of the instructions here your API should allow fetching the temperature and description. The OpenWeatherMap response provides a lot more information. The goal of this challenge is to expand the getWeather query type.

Challenge, expand your query to include the following properties:

  • feels_like
  • temp_min
  • temp_max
  • pressure
  • humidity

Challenge 9 - Handle Errors

The OpenWeatherMap API provides a cod property that includes an error code. If you provide a zipcode that doesn't exist you'll get a JSON object with a code of 404 and a message property with a message string. It might look something like:

{ cod: '404', message: 'city not found' }

Notice that '404' is a string. If you get a successful request the JSON will look like this:

{ ..., cod: 200 }

When COD is 200 it's a number!

Think about the results returned by your GraphQL API... What happens when you request this:

{
  getWeather(zip:99999) {
    temperature
  }
}

99999 is not a valid zip the JSON object from OpenWeatherMap will include "cod": "404" and "message":"city not found". All of the other information will be missing.

Think about the data types defined in your getWeather query Type...

In this case, you won't have the temperature. But you will have a message.

Your goal here is to return temperature, humidity, etc. sometimes, and include cod, and message sometimes. Don't overthink the solution (it may be easier than you first think). Talk it over with other students.

Here's a clue: if you make a query for temperature with an invalid zip code then the temperature should be null!

Here's what this situation might look like in code.

The Query might look like this:

{
  getWeather(zip:99999) {
    temperature
    description
    feels_like
    temp_min
    temp_max
    pressure
    humidity
    cod
    message
  }
}

The results would look like this:

{
  "data": {
    "getWeather": {
      "temperature": null,
      "description": null,
      "feels_like": null,
      "temp_min": null,
      "temp_max": null,
      "pressure": null,
      "humidity": null,
      "cod": 404,
      "message": "city not found"
    }
  }
}

Stretch Challenges 💪

Try as many of these stretch goals as you can!

After Class

  • Submit your answers to the Schema Pop Quiz to GradeScope
  • Submit your completed GraphQL + Express API project to GradeScope.
    • Solve as many of the challenges as you can!
    • If you have completed all of the challenges try the stretch challenges!

Evaluate your work

  1. Build a GraphQL API over a Public API
  2. Use GraphQL and Express
  3. Define Resolvers for types
- Does not meet expectations Meets Expectation Exceeds Expectations
Comprehension of Resolvers Can't explain what a resolver is Can describe a resolver Could teach another student what a resolver is
Using express-graphql Can't set up a simple server with express-graphql Could set up a simple server using express-graphql Could teach a new student how to set up a simple express-graphql server
Using Resolvers Can't write a simple resolver Could write a simple resolver Could expand on the resolvers from the homework solution to add new features and functionality

Resources