Skip to content

msidana/workshop-intro-to-graphql

Β 
Β 

Repository files navigation

πŸŽ“ Introduction to GraphQL + React + Java + Astra DB

Gitpod ready-to-code License Apache2 Discord

50 minutes, Beginner/Intermediate, Start Building

Both a simple graphQL enabled ReactJS app built using create-react-app AND a simple Java backend graphQL service built with Spring Initializr and using The Netflix DGS framework PLUS Astra DB hooked up and ready to rock! 😻

This is a companion to our Netflix Clone using Astra DB and GraphQL workshop and is essentially a "prologue" to that content. Once complete, feel free to to go deploy a Netflix clone using what you learned here.

Finally, this content uses React/JS concepts. If you are not familiar with those or need a refresher, take a look HERE to get up to date.

The materials have been built by the DataStax developer advocates team.

image

🎯 Objectives

  • An overview of what GraphQL is and what makes it cool
  • What differs between GraphQL, REST, other APIs, and their pros/cons
  • Hands-on examples of GraphQL queries and mutations
  • How to build GraphQL APIs for mobile and web applications
  • Setting up your Astra DB to store application data via GraphQL

ℹ️ Frequently asked questions ℹ️

  • Can I run the workshop on my computer?

There is nothing preventing you from running the workshop on your own machine. If you do so, you will need

You will have to adapt commands and paths based on your environment and install the dependencies by yourself. We won't provide support to keep on track with schedule. However, we will do our best to give you the info you need to be successful. This is considered a more advanced path to take.

  • What other prerequisites are there?
  • You will need a github account
  • You will need an Astra DB account, but we'll work through that in the exercises
  • Use Chrome or Firefox for the best experience.
  • Do I need to pay for anything for this workshop?
  • No. All tools and services we provide here are FREE.
  • Will I get a certificate if I attend this workshop?

Attending the session is not enough. You can earn yourself a nice badge to brag about if you complete all of the homework.

Materials for the Session

It doesn't matter if you join our workshop live or you prefer to do at your own pace, we have you covered. In this repository, you'll find everything you need for this workshop:

Homework

Don't forget to complete your upgrade and get your verified skill badge! Finish and submit your homework!

  1. Complete the practice steps from this repository as described below through step 9
  2. Insert (mutate) a show or genre of your choice in the database.
  3. Make a single screenshot of the React app with all of the working Astra DB sections
  4. Submit your homework here

That's it, you are done! Expect an email within about a week!

Let's start

Extra resources

graphql.org - The first place to learn about GraphQL

The Netflix DGS framework Tutorial - Java/Spring GraphQL backend (used to generate this code)

Spring Initializr - Used in the ^above tutorial to generate the Java/Spring backend starter

GraphiQL - GraphQL IDE included with The Netflix DGS Framework

Apollo client - Awesome GraphQL client for React/JS (not used here, but really solid, Netflix uses this)

Top 7 GraphQL IDEs - A nice collection of cool GraphQL IDEs to use

create-react-app tutorial - Create a React app from scratch (used to generate this code)

A Beginner's Guide to GraphQL - Ali Spittel's really awesome GraphQL starter video

Part 1 - DB Setup & Data Ingest

1. Login or Register to AstraDB and create database

ASTRADB is the simplest way to run Cassandra with zero operations at all - just push the button and get your cluster. No credit card required, FREE for roughly 5M writes, 30M reads, 40GB storage monthly - sufficient to run small production workloads.

βœ… Step 1a: Click the button to login or register with Datastax. You can use your Github, Google accounts or register with an email.

Make sure to chose a password with minimum 8 characters, containing upper and lowercase letters, at least one number and special character

  • Show me!

Use the following values when creating the database

Field Value
database name netflix_workshop_db
keyspace netflix_keyspace
Cloud Provider Use the one you like, click a cloud provider logo, pick an Area in the list and finally pick a region.

You can technically use whatever you want and update the code to reflect the keyspace. This is really to get you on a happy path for the first run.

You will see your new database pending OR initializing in the Dashboard.

image

The status will change to Active when the database is ready, this will only take 2-3 minutes. You will also receive an email when it is ready.

🏠 Back to Table of Contents

2. Create a security token

βœ… Step 2a: Create a token for your app to use in the settings screen. Use "Database Administrator" permission.

βœ… Step 2b: Copy the token value (eg AstraCS:KDfdKeNREyWQvDpDrBqwBsUB:ec80667c....) in your clipboard and save the CSV, this value would not be provided afterward.

πŸ‘οΈ Expected output

  • Show me!

🏠 Back to Table of Contents

3. Launch Gitpod

Gitpod is an 100% online IDE based on VS Code. To initialize your environment simply click on the button below (CTRL + Click to open in new tab) You will be asked for you github account, as needed.

Open in Gitpod

This will startup your demo environment. Be patient, it will take a couple minutes as everything loads up.

You may be asked if it's OK to launch a new tab (for the GraphiQL IDE that will be used subsequently. Click on Open to make sure the new tab opens as shown below.

image

4. Experiment with GraphiQL

It just so happens that The Netflix DGS framework comes with GraphiQL already integrated and ready for use. This is a wonderful tool you can use to explore graphQL queries and mutations. Let's experiement with this now!

Here's the schema defined in our java backend per graphql-backend-examples/src/main/resources/schema/schema.graphqls/schema.graphqls

type Query {
    shows(titleFilter: String): [Show]
    genres(labelFilter: String): [Genre]
}

type Show {
    title: String
    releaseYear: Int
}

type Genre {
    value: String!
}

Something to point out here is there is no database just yet. We are powering the graphQL schema via the back-end Java application and the graphQL data is completely hardcoded. Take a look at both ShowsDatafetcher.java and GenresDatafetcher.java located in graphql-backend-examples/src/main/java/com/example/demo to find the simple implementations using DGS annotations @DgsComponent and @DgsQuery.

Now, let's try out some graphQL queries

Plug these into the GraphiQL IDE that launched into a new tab from GitPod.

query justTitle {
  shows {
    title
  }
}
query withReleaseYear {
  shows {
    title
    releaseYear
  }
}
query getOneShow {
  shows (titleFilter: "Ozark") {
      title
      releaseYear
  }
}
query ShowsAndGenres {
  shows {
    title
    releaseYear
  }
  genres {
    value
  }
}

Screen Shot 2021-09-14 at 10 56 10 AM

5. Start up React

Ok, so we've played a bit with some graphQL queries on the backend and looked at how a basic schema works, but how do we hook this into our React JS app?

First, we need to run a couple commands to get things setup

In your GitPod IDE navigate to the workshop-intro-to-graphql/graphql-client-examples terminal on the bottom right (it should already be open for you).

βœ… Execute the following command

npm install -g netlify-cli

This will install the Netlify CLI (command line interface) which our React/JS app uses in conjunction with the serverless functions we've setup to talk to our graphQL endpoints.

βœ… Then, execute

netlify dev

This will start the React/JS application and display results from both the Shows and Genres graphQL queries and endpoints we were just experimenting with.

Compare javascript code to our graphQL queries from above

If you take a look at both getShowsBackend.js and getGenresBackend.js located in graphql-client-examples/functions you should notice that both use the same exact graphQL queries that we used above.

const query = `
    query getAllShows {
      shows {
        title
        releaseYear
      }
    }
  `
  const query = `
    query getAllGenres {
      genres {
        value
      }
    }
  `

All of the javascript wrapped around these is simply there to call the graphQL endpoint with the given query and pass the responseBody back to the calling function.

Now for the cool part

Take a look at Shows.js and Genres.js located in graphql-client-examples/src/components/Shows.js. In both cases they use React state, gqlResult

  const [gqlResult, setGqlResult] = useState(null)

to receive the responseBody from from our graphQL queries, set the React state, and inject the values dyanmically into the DOM. Check out the following javascript snippet from Shows.js.

// Asynchronously fetch any "shows" graphQL data from the Java backend
// using the getShowsAstra serverless function to call out to the
// Netflix DGS Java graphQL endpoint
const response = await fetch("/.netlify/functions/getShowsBackend", {
    method: "POST",
})
const responseBody = await response.json()
setGqlResult(responseBody) // on reponse set our graphQL result state

Notice how the fields (title, releaseYear) match our graphQL Shows schema exactly.

// Finally, if all other checks pass get the data
// from the payload via gqlResult state and inject it into the DOM
// Notice how the payload example below and the fields "title" and "releaseYear" match exactly
// {"data":{"shows":[{"title":"Stranger Things","releaseYear":2016},{"title":"Ozark","releaseYear":2017}...
return gqlResult.data.shows.map(({ title, releaseYear }) => (
    <div key={title}>
        <p>
        {title}: {releaseYear}
        </p>
    </div>
  ));

Notice how the field (value) matches our graphQL Genres schema exactly.

// Finally, if all other checks pass get the data
// from the payload via gqlResult state and inject it into the DOM
// Notice how the payload example below and the fields "title" and "releaseYear" match exactly
// {"data":{"genres":[{"value":"Action"},{"value":"Anime"}...
return gqlResult.data.genres.map(({ value }) => (
    <div key={value}>
        <p>
        {value}
        </p>
    </div>
  ));

6. Hook up the data layer with Astra DB

Ok, let's take this a step further and hook our app up to a data layer. As this point you should have already created your Astra DB database. Follow the instructions below to launch the GraphQL Playground provided in Astra

βœ… Step 6a: Open GraphQL Playground by

  1. Click on your active database
  2. Click Connect TAB
  3. Click GRAPHQL API
  4. Click link to your playground.

As show on the picture below. image

Note that values in the picture do no reflect the database name netflix_workshop_db, reason is we do not reproduce every picture each time

βœ… Step 6b: In GraphQL Playground, Populate HTTP HEADER variable x-cassandra-token on the bottom of the page with your token as shown below

βœ… Ensure you have the graphql-schema tab selected for this step

image

βœ… Step 6c: In GraphQL Playground, create a table with the following mutation, making sure to replace netflix_keyspace if you used a different name:

  • Copy the following mutation on the left panel
mutation {
  reference_list: createTable(
    keyspaceName:"netflix_keyspace",
    tableName:"reference_list",
    ifNotExists:true
    partitionKeys: [ 
      { name: "label", type: {basic: TEXT} }
    ]
    clusteringKeys: [
      { name: "value", type: {basic: TEXT}, order: "ASC" }
    ]
  )
}
  • Use the arrow in the middle of the screen to execute the query

image

🏠 Back to Table of Contents

7. Insert data in the Table with GraphQL

βœ… Step 7a: In graphQL playground, change tab to now use graphql. Edit the end of the URl to change from system to the name of your keyspace: netflix_keyspace

βœ… Step 7b: Populate HTTP HEADER variable x-cassandra-token on the bottom of the page with your token as shown below (again !! yes this is not the same tab)

image

βœ… Step 7c: In GraphQL Playground,populate the reference_list table with the following values

  • Copy the following mutation on the left panel
mutation insertGenres {
  action: insertreference_list(value: {label:"genre", value:"Action"}) {
    value{value}
  }
  anime: insertreference_list(value: {label:"genre", value:"Anime"}) {
     value{value}
  }
  award: insertreference_list(value: {label:"genre", value:"Award-Winning"}) {
     value{value}
  }
  children: insertreference_list(value: {label:"genre", value:"Children & Family"}) {
     value{value}
  }
  comedies: insertreference_list(value: {label:"genre", value:"Comedies"}) {
     value{value}
  }
  documentaries: insertreference_list(value: {label:"genre", value:"Documentaries"}) {
     value{value}
  }
  drama: insertreference_list(value: {label:"genre", value:"Dramas"}) {
     value{value}
  }
  fantasy: insertreference_list(value: {label:"genre", value:"Fantasy"}) {
     value{value}
  }
  french: insertreference_list(value: {label:"genre", value:"French"}) {
     value{value}
  }
  horror: insertreference_list(value: {label:"genre", value:"Horror"}) {
     value{value}
  }
  independent: insertreference_list(value: {label:"genre", value:"Independent"}) {
     value{value}
  }
  music: insertreference_list(value: {label:"genre", value:"Music & Musicals"}) {
     value{value}
  }
  romance: insertreference_list(value: {label:"genre", value:"Romance"}) {
     value{value}
  }
  scifi: insertreference_list(value: {label:"genre", value:"Sci-Fi"}) {
     value{value}
  }
  thriller: insertreference_list(value: {label:"genre", value:"Thriller"}) {
     value{value}
  }  
}
  • Use the arrow in the middle of the screen to execute the query

🏠 Back to Table of Contents

8. Retrieving list of values

βœ… Step 8a: In GraphQL Playground, not changing tab (yeah) list values from the table with the following query.

query getAllGenre {
    reference_list (value: {label:"genre"}) {
      values {
      	value
      }
    }
}

πŸ‘οΈ Expected output image

🏠 Back to Table of Contents

9. Hook the database up to our React/JS app

So, you just created a table, inserted (mutated) some rows into the table, and then retrieved all of the genres with the "getAllGenre" query using the GraphQL Playground provided as part of Astra DB. Now, let's hook our client up to our Astra DB graphQL endpiont and render the results to our website with React.

βœ… Step 9a: Configure database credentials

In your GitPod IDE navigate to the workshop-intro-to-graphql/graphql-client-examples terminal on the bottom right (it should already be open for you). This is running your nodejs/React app.

βœ… Execute the following command in your terminal to stop the React app

You will need to hold the control button and the letter C at the same time

CTRL-C 

βœ… Now execute the following command to configure the database

Note that this does require Node 15 and NPM 7 to work. You can install a node version manager like nvm or n to use multiple versions on your system. If you are using GitPod this should simply work since we pre-installed all of the dependcies for you.

npm exec astra-setup netflix_workshop_db netflix_keyspace

You will be asked to: Please paste the Database Admin Token here so copy over the Token you saved earlier, and hit enter. It will start with AstraCS:cvdPRONUrUUT:...

Screen Shot 2021-09-13 at 9 42 46 PM

This will add a set of envrionment variables for database authentication to your .env file at the root of workshop-intro-to-graphql/graphql-client-examples. It should look something like this.

Screen Shot 2021-09-13 at 9 45 41 PM

βœ… Start your React app back up with the following command

netlify dev

βœ… Step 9b: Verify data load

At this point your app should be running with a bunch of data displayed in the Shows, Genres, and ReferenceList sections, but notice the ShowsByName section displays "Error :("

Screen Shot 2021-09-13 at 10 00 26 PM

Can you figure out what's going on here?

Let's break this down.

  • We just added the database configuration and the ReferenceList section is populated which tells us our DB config and graphQL endpoints are configured properly

  • In the GraphQL Playground we added a schema for the reference_list table and added some data to the table, but we never created a schema for the ShowsByName section

  • If you take a look at the getShowsAstra.js script in graphql-client-examples/functions you can see the graphQL being used to query for data

exports.handler = async function (event) {
  const query = `
    query getAllShows {
      show_by_name {
        values {
          title
          releaseYear
        }
      }
    }
  `

βœ… Test this query in the GraphQL Playground graphQL tab

Copy this into the playground and press the "play" button to execute the query. NOTE, you can simply append the query to the end of the list and then choose the query you wish to execute when you hit the "play" button.

query getAllShows {
      show_by_name {
        values {
          title
          releaseYear
        }
      }
    }

Screen Shot 2021-09-13 at 10 22 16 PM

View Results

Notice what happened here. We have a validation error because there is no schema associated with the query we just executed. GraphQL uses a typed validation system so this is something to expect if a query is malformed, missing a schema, or something along those lines. You will want to control for this in your code.

Screen Shot 2021-09-13 at 10 25 31 PM

βœ… Step 9c: Create the ShowsByName table with a graphQL mutation to fix the app

Ok, so let's fix up the schema issue to resolve the error.

βœ… Execute the following mutation in the graph-schema tab of the GraphQL Playground

mutation CreateShowsTable {
  createTable(
    keyspaceName: "netflix_keyspace"
    tableName: "show_by_name"
    partitionKeys: [{
      name: "title", type: {basic:TEXT}
    }]
    values:[{
      name: "releaseYear", type: {basic:INT}
    }]
  )
}

Screen Shot 2021-09-13 at 10 34 26 PM

βœ… Verify result

Once executed you should see a result like this

Screen Shot 2021-09-13 at 10 34 34 PM

βœ… Add some data

Now, go back to the graphql tab of the GraphQL Playground and add the following mutation

mutation insertShows {
  stranger: insertshow_by_name (
    value: {
      title: "Stranger Things",
      releaseYear: 2016}) {
  	value{title}
  }
  ozark: insertshow_by_name (
    value: {
      title: "Ozark",
      releaseYear: 2017}) {
  	value{title}
  }
}

Screen Shot 2021-09-13 at 10 39 50 PM

βœ… Check the result

Screen Shot 2021-09-13 at 10 39 58 PM

βœ… Finally, refresh your React app

Notice this no longer displays "Error :(", but now correctly displays the data you just inserted (mutated). It might be fun to add some of your own data to this schema and refresh your page.

Screen Shot 2021-09-13 at 10 41 03 PM

Feel free to experiment with a couple more graphQL queries now that you have some data in the table

Notice in this case we are passing a "title" parameter "Ozark" to only return the show values that match that title.

query getOneShow {
  show_by_name (value: {title: "Ozark"}) {
    values {
      title
      releaseYear
    }
  }
}
query getAllShows {
  show_by_name {
    values {
      title
      releaseYear
    }
  }
}

That's it, you did it! Nice job!

We hope this workshop gave you enough information on GraphQL to be dangerous and start you on a journey to using GraphQL in your own apps. Also, don't forget to do the HOMEWORK

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 66.8%
  • Java 20.6%
  • HTML 7.5%
  • CSS 4.1%
  • Shell 1.0%