{"id":15056716,"url":"https://github.com/datastaxdevs/workshop-graphql-netflix","last_synced_at":"2025-04-12T19:43:42.874Z","repository":{"id":37073999,"uuid":"372483076","full_name":"datastaxdevs/workshop-graphql-netflix","owner":"datastaxdevs","description":"Workshop to illustrate how to use GraphQL","archived":false,"fork":false,"pushed_at":"2023-05-05T18:21:27.000Z","size":51143,"stargazers_count":619,"open_issues_count":525,"forks_count":304,"subscribers_count":19,"default_branch":"master","last_synced_at":"2025-04-03T22:09:28.255Z","etag":null,"topics":["astradb","cassandra","gitpod","graphql","netlify","nosql","reactjs"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/datastaxdevs.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-05-31T11:29:32.000Z","updated_at":"2025-02-25T18:47:04.000Z","dependencies_parsed_at":"2024-10-29T21:24:54.351Z","dependency_job_id":null,"html_url":"https://github.com/datastaxdevs/workshop-graphql-netflix","commit_stats":{"total_commits":153,"total_committers":14,"mean_commits":"10.928571428571429","dds":0.6928104575163399,"last_synced_commit":"eb087e639090e1b017889b871f2db9b6820ccb08"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-graphql-netflix","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-graphql-netflix/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-graphql-netflix/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/datastaxdevs%2Fworkshop-graphql-netflix/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/datastaxdevs","download_url":"https://codeload.github.com/datastaxdevs/workshop-graphql-netflix/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248625479,"owners_count":21135512,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["astradb","cassandra","gitpod","graphql","netlify","nosql","reactjs"],"created_at":"2024-09-24T21:55:32.756Z","updated_at":"2025-04-12T19:43:42.846Z","avatar_url":"https://github.com/datastaxdevs.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003c!--- STARTEXCLUDE ---\u003e\n# 🎓 Netflix Clone using Astra DB and GraphQL\n\n[![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/from-referrer/)\n[![License Apache2](https://img.shields.io/hexpm/l/plug.svg)](http://www.apache.org/licenses/LICENSE-2.0)\n[![Discord](https://img.shields.io/discord/685554030159593522)](https://discord.com/widget?id=685554030159593522\u0026theme=dark)\n\n*50 minutes, Intermediate, [Start Building](#1-login-or-register-to-astradb-and-create-database)*\n\nA simple **ReactJS** Netflix homepage clone running on *Astra DB* that leverages the GraphQL API with *paging* and *infinite scrolling.*\nThis application is the result of the collaboration between [Ania Kubow](https://www.youtube.com/channel/UC5DNytAJ6_FISueUfzZCVsw) and the Datastax Developer Advocate team.\n\n\u003c!--- ENDEXCLUDE ---\u003e\n\nSee the [Video Demo](https://imgur.com/3ns3UJB) of what you will build!\n\n![image](https://github.com/datastaxdevs/workshop-graphql-netflix/raw/master/images/ui.png)\n\n## 🎯 Objectives\n* Build and run a Netflix clone.\n* Learn **GraphQL API** and how to use it with a database to create the tables and navigate the data.\n* Learn about **pagination** and **infinite scrolling** in a Web UI.\n* Leverage Netlify and DataStax Astra DB.\n* Deploy the Netflix clone to production with Netlify.\n\n## ℹ️ Frequently asked questions ℹ️\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eCan I run the workshop on my computer?\u003c/strong\u003e\u003c/summary\u003e\n\nThere is nothing preventing you from running the workshop on your own machine.\nIf you do so, you will need\n* git installed on your local system\n* [node 15 and npm 7 or later](https://www.whitesourcesoftware.com/free-developer-tools/blog/update-node-js/)\n\nYou 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.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eWhat other prerequisites are there?\u003c/strong\u003e\u003c/summary\u003e\n\n* You will need a github account\n* You will also need Netlify and Astra DB accounts, but we'll work through that in the exercises\n* Use **Chrome** or **Firefox** for the best experience. Other browsers are great, but don't work well with the GitPod integration we use a bit later.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eDo I need to pay for anything for this workshop?\u003c/strong\u003e\u003c/summary\u003e\n\n**No.** All tools and services we provide here are FREE.\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eWill I get a certificate if I attend this workshop?\u003c/strong\u003e\u003c/summary\u003e\n\nYes, but attending the session is not enough. You need to complete the homeworks detailed below and you will get a participation badge.\n\n\u003c/details\u003e\n\n\n## Materials for the Session\n\nIt 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:\n\n- [Slide deck](slides/slides.pdf)\n- [Discord chat](https://dtsx.io/discord)\n- [\"cassandra\" on StackOverflow](https://stackoverflow.com/questions/tagged/cassandra)\n- [\"cassandra\" on DBA StackExchange](https://dba.stackexchange.com/questions/tagged/cassandra)\n\n\n# Let's start\n\n## Table of contents\n\n### Part I - DB Setup \u0026 Data Ingest\n1. [Create Astra DB Instance](#1-login-or-register-to-astradb-and-create-database)\n2. [Create a security token](#2-create-a-security-token)\n3. [Create table for genres with GraphQL](#3-create-table-for-genres-with-graphql)\n4. [Insert genre data with GraphQL](#4-insert-genre-data-with-graphql)\n5. [Retrieve genres with GraphQL](#5-retrieve-genres-with-graphql)\n6. [Create a table for movies](#6-create-a-table-for-movies)\n7. [Insert a few movies](#7-insert-a-few-movies)\n8. [Retrieve movies: Pagination](#8-retrieve-movies-pagination)\n\n### Part II - Build and Deploy Front-End\n\n1. [Deploy skeletal GUI to Netlify](#1-deploy-skeletal-gui-to-netlify)\n2. [Launch Gitpod from YOUR Github repo](#2-launch-gitpod-from-your-github-repo)\n3. [Set up and use `astra-cli`](#3-set-up-and-use-astra-cli)\n4. [Serverless Functions](#4-serverless-functions)\n5. [Fetching from the Front-End](#5-fetching-from-the-front-end)\n6. [Install the Netlify CLI](#6-install-the-netlify-cli)\n7. [Provide DB connection parameters](#7-provide-db-connection-parameters)\n8. [Run the app in dev mode](#8-run-the-app-in-dev-mode)\n9. [Connect to your Netlify site](#9-connect-to-your-netlify-site)\n10. [Deploy in production!](#10-deploy-in-production)\n\n[**🎓 Complete the assignment, receive your Badge!**](#homework)\n\n### Extra resources\n\n- [Intro to GraphQL Workshop](https://github.com/datastaxdevs/workshop-intro-to-graphql)\n- [React starter using NPX](https://github.com/datastaxdevs/react-basics)\n- [React ToDo app](https://github.com/datastaxdevs/appdev-week1-todolist)\n- [What is JamStack?](https://github.com/datastaxdevs/workshop-battlestax/blob/master/README_JAM.md)\n- [Video tutorial with Ania Kubow](#video-tutorial-with-ania-kubow)\n\n# Part 1 - DB Setup \u0026 Data Ingest\n\n## 1. Login or Register to AstraDB and create database\n\n\u003e 🎁 *When creating your instance, use the promotion code **ANIA200** to get 200$ of additional free credit!*\n\n_**`ASTRA DB`** is the simplest way to run Cassandra with zero operations at all - just push the button and get your cluster. No credit card required, 40M read/write operations and about 80GB storage monthly for free - sufficient to run small production workloads. If you use up your credits the databases will pause, no charge, and you will be given the option to upgrade to a higher tier._\n\nLeveraging [Database creation guide](https://awesome-astra.github.io/docs/pages/astra/create-instance/#c-procedure) create a database. **Right-Click** the following button and *Open in a new TAB.*\n\n\u003ca href=\"https://astra.dev/yt-01-04\" target=\"blank\"\u003e\u003cimg src=\"https://github.com/datastaxdevs/workshop-graphql-netflix/raw/master/images/create_astra_db.png?raw=true\" /\u003e\u003c/a\u003e\n\n|Field|Value|\n|---|---|\n|**Database Name**| `workshops`|\n|**Keyspace Name**| `netflix`|\n|**Regions**| Select `GOOGLE CLOUD`, then an Area close to you, then a region with no LOCK 🔒 icons: the LOCKed regions are the region not accessible to the Free Tier.|\n\n\u003e **ℹ️ Note:** If you already have a database `workshops`, simply add a keyspace `netflix` using the `Add Keyspace` button on the bottom right hand corner of the DB Dashboard page. You may have to \"Resume\" the database first in case it is in \"hibernated\" state.\n\nWhile the database is being created, you will also get a **Security token** (needed to authenticate with your database and start using it):\n**please IGNORE THIS ONE, as we will be soon creating a new, more powerful token for today**.\n\nThe status will change from `Pending` to `Active` when the database is ready, this usually only takes 2-3 minutes.\n\n\n## 2. Create a security token\n\n\u003e Note: this step is very important, as the token generated automatically for you with\n\u003e the database lacks some permissions we'll use in the workshop.\n\n[Create a token for your app](https://awesome-astra.github.io/docs/pages/astra/create-token/#c-procedure), _using the **\"Database Administrator\"** role_.\nKeep it handy for later use (best to download it in CSV format, as the values\nwill not be visible afterward).\nThis will provide authentication later when interacting with the database.\nToday, in particular, you'll need the string labeled \"token\" (the one starting with `AstraCS:...`).\n\n\u003e **⚠️ Important**\n\u003e The instructor will show the token creation on screen,\n\u003e but will then destroy it immediately for security reasons.\n\n\n## 3. Create table for genres with GraphQL\n\n✅  **Step 3a:** Open **GraphQL Playground**:\n\n0. Ensure you are logged on to your [Astra](https://astra.datastax.com) account\n1. Click on the \"workshops\" database on the left (expanding the list if needed)\n2. Click `Connect` TAB\n3. Click the `APIs`  connection method\n4. Make sure `GraphQL API` is selected\n5. Locate the link to your GraphQL Playground in the text\n\n![Open Astra DB GraphQL Playground image](https://github.com/datastaxdevs/workshop-graphql-netflix/raw/master/images/open-playground-2-wh.png)\n\n\u003cdetails\u003e\n\u003csummary\u003e\u003cstrong\u003eClick here if you are not using the \"New Astra Experience\" UI (yet)\u003c/strong\u003e\u003c/summary\u003e\n\n![Open Astra DB GraphQL Playground image, old Astra UI](images/open-playground-2.png)\n\n\u003c/details\u003e\n\n**Note**: in the following, we will refer to \"playground tabs\". These are _not_ the tabs\nin your browser, rather they are tabs _within_ the Playground application,\nto switch between the (logically distinct) realms of \"managing schema\" and \"managing data in the tables\"\n(more on that later).\n\n![Playground tabs VS Browser tabs](https://github.com/datastaxdevs/workshop-graphql-netflix/raw/master/images/tabs-vs-playgroundtabs-labeled-2.png)\n\n✅  **Step 3b:** Provide the database token as header\n\nIn the GraphQL Playground, **Populate HTTP HEADER** variable `x-cassandra-token` on the bottom of the page with your token (including the `AstraCS:` part).\n_This is the \"Database Administrator\" token you created earlier on the Astra DB dashboard (Step 2 above)._\n\n\n\u003cdetails\u003e\n\u003csummary\u003e\n\u003cstrong\u003eNote\u003c/strong\u003e: make sure you are on the \u003cstrong\u003egraphql-schema\u003c/strong\u003e playground tab in this step. Click here to show image.\n\u003c/summary\u003e\n\n![GraphQL Playground and token header, Schema playground tab](images/graphql-playground.png)\n\n\u003c/details\u003e\n\n\u003e Note: the GraphQL Playground starts with a ready-to-use _temporary token_ as the `x-cassandra-token` header. But you want the queries run in the Playground\n\u003e to be identical to those that the Netlify functions will run from code, so **please replace the token with your DB token as instructed**.\n\n✅  **Step 3c:** In GraphQL Playground, create the `reference_list` table:\n\nCopy the following **mutation** to the left panel\n\n```yaml\nmutation createReferenceList {\n  reference_list: createTable(\n    keyspaceName:\"netflix\",\n    tableName:\"reference_list\",\n    ifNotExists:true\n    partitionKeys: [ \n      { name: \"label\", type: {basic: TEXT} }\n    ]\n    clusteringKeys: [\n      { name: \"value\", type: {basic: TEXT}, order: \"ASC\" }\n    ]\n  )\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eand then use the big \"play button\" arrow in the center to execute it. Click for screenshot.\u003c/summary\u003e\n\n![image](images/playground-1.png)\n\n\u003c/details\u003e\n\n\n**GraphQL Playground troubleshooting** (covers this whole section)\n\n|Trouble| Shooting|\n|---|---|\n|Server cannot be reached | Add Astra token to headers (including `AstraCS:...`; check quotes) |\n|Server cannot be reached (second playground tab) | Check playground target URL ends with `netflix` |\nResponse not successful: Received status code 401 | Same as \"server cannot be reached\" |\n| Response not successful: Received status code 404 | Check spelling of keyspace in target URL |\n|\"Play\" button does nothing| Ensure query is syntactically correct |\n\"Validation error of type FieldUndefined\" | Most likely query in the wrong playground tab, or writing to table not created yet |\n\n## 4. Insert genre data with GraphQL\n\n✅  **Step 4a:** Get to the API URL for your keyspace\n\nIn graphQL playground, **change playground tab** to now use `graphql`. The Playground has its own address bar\n(**note**: it's _not_ the address bar of your browser). Edit the ending of the URL shown there, from `system` to the\nname of the keyspace: `netflix`\n\n✅  **Step 4b:** Repeat the insertion of the `x-cassandra-token` header for this playground tab (as you did for the first one):\n\n\u003cdetails\u003e\n\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![image](images/graphql-playground-2.png)\n\n\u003c/details\u003e\n\n✅  **Step 4c:** In the GraphQL Playground, run the mutation that writes genre data:\n\nCopy the following mutation on the left panel:\n\n```yaml\nmutation insertGenres {\n  action: insertreference_list(value: {label:\"genre\", value:\"Action\"}) {\n    value{value}\n  }\n  anime: insertreference_list(value: {label:\"genre\", value:\"Anime\"}) {\n     value{value}\n  }\n  award: insertreference_list(value: {label:\"genre\", value:\"Award-Winning\"}) {\n     value{value}\n  }\n  children: insertreference_list(value: {label:\"genre\", value:\"Children \u0026 Family\"}) {\n     value{value}\n  }\n  classic: insertreference_list(value: {label:\"genre\", value:\"Classic\"}) {\n     value{value}\n  } \n  comedies: insertreference_list(value: {label:\"genre\", value:\"Comedies\"}) {\n     value{value}\n  }\n  crime: insertreference_list(value: {label:\"genre\", value:\"Crime\"}) {\n     value{value}\n  } \n  cult: insertreference_list(value: {label:\"genre\", value:\"Cult\"}) {\n     value{value}\n  }  \n  documentaries: insertreference_list(value: {label:\"genre\", value:\"Documentaries\"}) {\n     value{value}\n  }\n  drama: insertreference_list(value: {label:\"genre\", value:\"Dramas\"}) {\n     value{value}\n  }\n  fantasy: insertreference_list(value: {label:\"genre\", value:\"Fantasy\"}) {\n     value{value}\n  }\n  french: insertreference_list(value: {label:\"genre\", value:\"French\"}) {\n     value{value}\n  }\n  horror: insertreference_list(value: {label:\"genre\", value:\"Horror\"}) {\n     value{value}\n  }\n  independent: insertreference_list(value: {label:\"genre\", value:\"Independent\"}) {\n     value{value}\n  }\n  international: insertreference_list(value: {label:\"genre\", value:\"International\"}) {\n     value{value}\n  } \n  italian: insertreference_list(value: {label:\"genre\", value:\"Italian\"}) {\n     value{value}\n  } \n  musicmusicals: insertreference_list(value: {label:\"genre\", value:\"Music \u0026 Musicals\"}) {\n     value{value}\n  } \n  realitytv: insertreference_list(value: {label:\"genre\", value:\"Reality TV\"}) {\n     value{value}\n  } \n  romance: insertreference_list(value: {label:\"genre\", value:\"Romance\"}) {\n     value{value}\n  }\n  scifi: insertreference_list(value: {label:\"genre\", value:\"Sci-Fi\"}) {\n     value{value}\n  }\n  thriller: insertreference_list(value: {label:\"genre\", value:\"Thriller\"}) {\n     value{value}\n  } \n  tvshow: insertreference_list(value: {label:\"genre\", value:\"TV Show\"}) {\n     value{value}\n  } \n}\n```\n\nthen click on the big \"play button\" arrow in the center to execute the mutation\n\n## 5. Retrieve genres with GraphQL\n\n✅  **Step 5a:** In GraphQL Playground, not changing playground tab (stay on the second: \"graphql\", yeah) run the following query to read the `value` column of all table rows:\n\n```yaml\nquery getAllGenres {\n    reference_list (value: {label:\"genre\"}) {\n      values {\n      \tvalue\n      }\n    }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\nShow me!\n\u003c/summary\u003e\n\n![image](images/graphql-playground-3.png)\n\n\u003c/details\u003e\n\n## 6. Create a table for movies\n\n✅  **Step 6a:** Switch back to first playground tab (\"graphql-schema\"; the token header will be already set).\n\n\u003cdetails\u003e\n\u003csummary\u003e\nClick for screenshot\n\u003c/summary\u003e\n\n![image](images/graphql-back.png)\n\n\u003c/details\u003e\n\nUse the following mutation to create a new table:\n\n```yaml\nmutation createMoviesTable {\n  movies_by_genre: createTable(\n    keyspaceName:\"netflix\",\n    tableName:\"movies_by_genre\",\n    ifNotExists: true,\n    partitionKeys: [\n      { name: \"genre\", type: {basic: TEXT} }\n    ]\n    clusteringKeys: [ \n      { name: \"year\", type: {basic: INT}, order: \"DESC\" },\n      { name: \"title\", type: {basic: TEXT}, order: \"ASC\" }\n    ]\n    values: [\n      { name: \"synopsis\", type: {basic: TEXT} },\n      { name: \"duration\", type: {basic: INT} },\n      { name: \"thumbnail\", type: {basic: TEXT} }\n    ]\n  )\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\nShow me!\n\u003c/summary\u003e\n\n![image](images/graphql-playground-4.png)\n\n\u003c/details\u003e\n\n## 7. Insert a few movies\n\n✅  **Step 7a:** Go to playground tab \"graphql\" again. \n\n\u003cdetails\u003e\n\u003csummary\u003e\nClick for screenshot\n\u003c/summary\u003e\n\n![image](images/graphql-playground-2.png)\n\n\u003c/details\u003e\n\nUse the following mutation to populate the `movies_by_genre` table with four movies:\n\n```yaml\nmutation insertMovies {\n  inception: insertmovies_by_genre(\n    value: { \n      genre:\"Sci-Fi\", \n      year:2010,\n      title:\"Inception\",\n      synopsis:\"Cobb steals information from his targets by entering their dreams.\",\n      duration:121,\n      thumbnail:\"https://i.imgur.com/RPa4UdO.mp4\"}) {\n        value{title}\n    }\n  \n  prometheus: insertmovies_by_genre(value: { \n      genre:\"Sci-Fi\", \n      year:2012,\n      title:\"Prometheus\",\n      synopsis:\"After a clue to mankind's origins is discovered, explorers are sent to the darkest corner of the universe.\",\n      duration:134,\n      thumbnail:\"https://i.imgur.com/L8k6Bau.mp4\"}) {\n        value{title}\n    }\n  \n  \taliens: insertmovies_by_genre(value: { \n      genre:\"Sci-Fi\", \n      year:1986,\n      title:\"Aliens\",\n      synopsis:\"Ellen Ripley is sent back to the planet LV-426 to establish contact with a terraforming colony.\",\n      duration:134,\n      thumbnail:\"https://i.imgur.com/QvkrnyZ.mp4\"}) {\n        value{title}\n    }\n  \n    bladeRunner: insertmovies_by_genre(value: { \n      genre:\"Sci-Fi\", \n      year:1982,\n      title:\"Blade Runner\",\n      synopsis:\"Young Blade Runner K's discovery of a long-buried secret leads him to track down former Blade Runner Rick Deckard.\",\n      duration:145,\n      thumbnail:\"https://i.imgur.com/xhhvmj1.mp4\"}) {\n        value{title}\n    }\n  }\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\nShow me!\n\u003c/summary\u003e\n\n![image](images/graphql-playground-5.png)\n\n\u003c/details\u003e\n\n\n## 8. Retrieve movies: Pagination\n\n✅  **Step 8a:** In GraphQL Playground, not changing playground tab (stay on the second tab, \"graphql\", yeah) list values from the table with the following command:\n\n```yaml\nquery getMovieAction {\n  movies_by_genre (\n      value: {genre:\"Sci-Fi\"},\n      orderBy: [year_DESC]\n  ) {\n    values {\n      year,\n      title,\n      duration,\n      synopsis,\n      thumbnail\n    }\n  }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\nShow me!\n\u003c/summary\u003e\n\n![image](images/graphql-playground-6.png)\n\n\u003c/details\u003e\n\n\n✅ **Step 8b: Enable pagination:** On a small dataset, you can retrieve all values in the table at once; but in general, for performance or network reasons, you'll need pagination. Run a similar query as before, but this time asking for a _page size of 2_:\n\n```yaml\nquery getMovieActionPag1 {\n    movies_by_genre (\n        value: {genre:\"Sci-Fi\"},\n        options: {pageSize: 2},\n        orderBy: [year_DESC]\n    ) {\n      values {\n        year,\n        title,\n        duration,\n        synopsis,\n        thumbnail\n      }\n    pageState\n    }\n}\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\nShow me!\n\u003c/summary\u003e\n\n![image](images/playground-2.png)\n\n\u003c/details\u003e\n\n\n✅ **Step 8c: Fetch the next page:**\n\nNotice that `pageState` now is also returned. Use it to fetch the next 2 items (next page):\nedit the next query to replace `YOUR_PAGE_STATE` with your own string value:\n\n```yaml\nquery getMovieActionNextPage {\n    movies_by_genre (\n        value: {genre:\"Sci-Fi\"},\n        options: {pageSize: 2, pageState: \"YOUR_PAGE_STATE\"},\n        orderBy: [year_DESC]\n    ) {\n      values {\n        year,\n        title,\n        duration,\n        synopsis,\n        thumbnail\n      }\n    pageState\n    }\n}\n```\n \n\u003cdetails\u003e\n\u003csummary\u003e\nShow me!\n\u003c/summary\u003e\n\n![image](images/playground-3.png)\n\n\u003c/details\u003e\n\n\nIf you try to paste the _newly-obtained_ value for `pageState` and re-run the query, you get an empty list and a null `pageState` in return. D'oh! You had scrolled through all rows already:\n_this is how pagination signals the end of the full results list._\n\n# Part 2 - Build and Deploy Front-End\n\n\n## 1. Deploy skeletal GUI to Netlify\n\n✅ **Step 1a: Netlify Button:** Click the following button to deploy the skeletal GUI to Netlify. There is no data since there is no database connected to the app (yet); we will connect the database to the app shortly.\n\n**Note**: preferrably Ctrl-click for a new tab.\n\n\u003cdetails\u003e\n\u003csummary\u003e\nWhat does the Netlify deploy button do?\n\u003c/summary\u003e\n\nThe Netlify deploy button will:\n\n- Create a new repository for you on Github (Note: it's an unrelated _copy_, not a fork)\n- Create a site on Netlify (and deploy a nonworking build of the app, which lacks the DB connection parameters still)\n- Link the two together.\n\n\u003c/details\u003e\n\n[![Deploy to Netlify](https://www.netlify.com/img/deploy/button.svg)](https://app.netlify.com/start/deploy?repository=https://github.com/datastaxdevs/workshop-graphql-netflix)\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Netlify button in action](images/deploy-to-netlify.gif)\n\n\u003c/details\u003e\n\nThis will take a few minutes:\n\n- you may have to authenticate through Github in the process;\n- confirm the repo name and \"Save \u0026 Deploy\" when asked.\n\n_Note: if there is an existing account in Netlify, check the settings to make sure the Netlify account is connected to your Github account._\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Deploy to Netlify, \"connected accounts\"](images/netlify-connect-01.png)\n\n\u003c/details\u003e\n\n✅ **Step 1b: Check the deploy logs:** Click on `Site deploy in progress` within the Netlify UI.\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Deploy to Netlify, \"site deploy in progress\"](images/deploy-1.png)\n\n\u003c/details\u003e\n\nThen click the top deploy link to see the build process.\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Deploy to Netlify, \"Production/Building\"](images/deploy-2.png)\n\n\u003c/details\u003e\n\n✅ **Step 1c: Complete the build:** Wait until the build shows `Netlify Build Complete`,  **When you see \"_Pushing to repository..._\"** you're ready to move on.\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Deploy to Netlify, logs showing build finishing](images/deploy-3b.png)\n\n\u003c/details\u003e\n\n✅ **Step 1d: Get back to your new site:** Scroll up to the top and click on the site name (it'll be after \"_[your login]_'s Team\" next to the Netlify button). Then locate your app's full URL and click to open it.\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Deploy to Netlify, site name next to your team's name](images/deploy-4.png)\n\nClicking on the full URL (something like `https://YOUR-SITE-NAME.netlify.app`) you will see the skeletal GUI (without the data from the database) in a new tab. Here is where to click:\n\n![Deploy to Netlify, access the skeletal GUI](images/deploy-4sitename.png)\n\nand here, finally, your skeletal GUI in its full splendour:\n\n![Netlify site, no data yet (skeletal GUI)](images/deploy-4skeletal.png)\n\n\u003c/details\u003e\n\n## 2. Launch Gitpod from YOUR Github repo\n\n✅ **Step 2a: Jump to YOUR repo:** Click on the `GitHub` in `Deploys from GitHub` to get to your new repository on Github.\nScroll to where you were in the README.\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Deploy to Netlify, ](images/deploy-5.png)\n\n\u003c/details\u003e\n\n\u003e **Note** At this point, you MUST be reading this README from **YOUR** Github repository.\n\u003e That is, if the address bar still says `https://github.com/datastaxdevs/...` please\n\u003e head over to YOUR copy of the repo before going the Gitpod route!\n\n✅ **Step 2b: Launch Gitpod:**\n\nUse this link to open Gitpod from **YOUR** repository! (_Tip: Ctrl-click on the button to open in new tab._)\n\n[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/from-referrer/)\n\n_Note: the button works on \u003cimg src=\"images/chrome-logo.svg\" height=\"20\"/\u003e Chrome and \u003cimg src=\"images/firefox-logo.svg\" height=\"20\"/\u003e Firefox._\n\n\u003cdetails\u003e\n\u003csummary\u003eClick to troubleshoot if you have another browser\u003c/summary\u003e\n\n\u003cimg src=\"images/gitpod_trick.png\" /\u003e\n\n\u003c/details\u003e\n\nℹ️ _It may take a few minutes (approx. 3-5) for GitPod to fully initialize.\nPlease wait until the console in the lower half of Gitpod is responsive._\n\n\u003e You may see a dialog about \"opening this workspace in VS Code Desktop\": you can safely dismiss it.\n\nGitpod will be your IDE from now on. If you are familiar with VSCode, you can probably\njust use it. Otherwise, take a moment to review a separate page\n[\"Know your Gitpod\"](know_your_gitpod.md)\nand then come back here.\n\n## 3. Set up and use `astra-cli`\n\nYou are going to use a CLI tool to simplify operations with Astra DB. The tool\nis [preinstalled](https://docs.datastax.com/en/astra-classic/docs/astra-cli/installation.html)\non your Gitpod.\n\n✅ **Step 3a: Set up the CLI:**\n\nRun the following in the Gitpod terminal and,\nwhen prompted, enter the `AstraCS:...` you obtained at the beginning.\n\n``` bash\nastra setup\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\nShow me!\n\u003c/summary\u003e\n\n![astra-cli](images/astra-cli-setup-2.png)\n\n\u003c/details\u003e\n\n\n✅ **Step 3b: Bulk data load:** Load a large movie dataset in the database.\nThis command installs and properly launches the `DSBulk` tool ([docs](https://docs.datastax.com/en/dsbulk/docs/dsbulkAbout.html)):\n\n``` bash\nastra db load workshops \\\n  -url data/movies_by_genre.csv \\\n  -k netflix \\\n  -t movies_by_genre\n```\n\n\u003cdetails\u003e\n\u003csummary\u003e\nShow me!\n\u003c/summary\u003e\n\n![astra-cli](images/astra-cli-dsbulk-2.png)\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eShow the syntax for old versions of astra-cli (click here)\u003c/summary\u003e\n\nNote: you should not need this.\n\n```bash\nastra db dsbulk workshops load \\\n  -url data/movies_by_genre.csv \\\n  -k netflix \\\n  -t movies_by_genre\n```\n\n\u003c/details\u003e\n\n\u003e *Note*: we mock the trailers for these thousands of movies by using a handful\n\u003e of them over and over. Don't be surprised if you'll see the wrong trailers\n\u003e for your favorite movie!\n\nThat's it! All 6000+ movies are now loaded and ready to go!\n\n## 4. Serverless Functions\n\n\u003e _Note_: this section and the next one (\"Fetching from the Front-End\")\n\u003e are not steps to \"perform\", rather suggestions to dive in the app\n\u003e code and figure out how the various parts (React components, Netlify\n\u003e functions and finally the GraphQL server in Astra DB) fit together.\n\u003e **You can skip these and jump to the [next practical step](#6-install-the-netlify-cli)**\n\u003e if you are in a hurry, but please come back to these\n\u003e for reference if you want to dissect the code!\n\n\u003cdetails\u003e\u003csummary\u003eShow me this section\u003c/summary\u003e\n\nTake a look at `functions/getGenres.js`\n\n```javascript\nconst fetch = require('node-fetch')\n\nexports.handler = async function (event) {\n\n  const body = JSON.parse(event.body)\n  const url = process.env.ASTRA_DB_GRAPHQL_URL\n  const query = `\n    query getAllGenres {\n      reference_list (\n        value: { label: \"genre\"},\n        options: {\n          pageSize: ${JSON.stringify(body.pageSize)},\n          pageState: ${JSON.stringify(body.pageState)}\n        }\n      ) {\n        values {\n          value\n        }\n        pageState\n      }\n    }\n  `\n\n  const response = await fetch(url, {\n    method: 'POST',\n    headers: {\n      \"Content-Type\": \"application/json\",\n      \"x-cassandra-token\": process.env.ASTRA_DB_APPLICATION_TOKEN\n    },\n    body: JSON.stringify({ query })\n  })\n\n  try {\n    const responseBody = await response.json()\n    return {\n      statusCode: 200,\n      body: JSON.stringify(responseBody)\n    }\n  } catch (e) {\n    console.log(e)\n    return {\n      statusCode: 500,\n      body: JSON.stringify(e)\n    }\n  }\n}\n```\n\nYou'll notice the familiar GraphQL query \"getAllGenres\" you used previously in the playground.\nIt's been modified a bit to utilize pagination.\nWhen building the GraphQL query, you pass the desired page size and state to the GraphQL API:\n\n```javascript\noptions: {\n  pageSize: ${JSON.stringify(body.pageSize)},\n  pageState: ${JSON.stringify(body.pageState)}\n}\n```\n\nYou ask the API server to give us back the table column \"value\" (containing the genre name), but also the page state\nfor when you'll need the next page:\n\n```javascript\n{\n  values {\n    value\n  }\n  pageState\n}\n```\n\nThe serverless function `functions/getMovies.js` works in much the same way, but you provide the specific genre you want and the page size is hardcoded:\n\n```javascript\nquery {\n  movies_by_genre (\n    value: { genre: ${JSON.stringify(genre)}},\n    orderBy: [year_DESC],\n    options: { pageSize: 6, pageState: ${JSON.stringify(pageState)} }\n  ) {\n    values {\n      year,\n      title,\n      duration,\n      synopsis,\n      thumbnail\n    }\n    pageState\n  }\n}\n```\n\n\u003c/details\u003e\n\n## 5. Fetching from the Front-End\n\n\u003cdetails\u003e\u003csummary\u003eShow me this section\u003c/summary\u003e\n\nTake a look at how you fetch from these serverless functions from the front-end. Start in `src/App.js`\n\nThere is a fetch method defined, that will retrieve a page of genres by calling the `getGenres` serverless function.\n\n```javascript\nconst fetchData = async () =\u003e {\n  if (! isFetching)  {\n    setIsFetching(true)\n    const response = await fetch(\"/.netlify/functions/getGenres\", {\n      method: \"POST\",\n      body: JSON.stringify({pageState, pageSize}),\n    })\n    const responseBody = await response.json()\n    setPageState(responseBody.data.reference_list.pageState)\n    setGenres(gs =\u003e (gs || []).concat(responseBody.data.reference_list.values))\n    setIsFetching(false)\n  }\n}\n```\n\nYou pass in the current `pageState` and `pageSize` state variables and receive a response from the serverless function. You then set the `pageState` var to the new pagestate, and set the `genres` state variable to the received data. (Note that you are concatenating the new data to the var, since you want to keep all previously fetched data, not replace).\n\nWhen rendering the page, you generate a `\u003cSection\u003e` component for each genre, plus a `\u003cdiv\u003e` at the bottom, which will detect a `mouseEnter` event and trigger the loading of a new pageful of genres:\n\n```javascript\n\u003c\u003e\n  \u003cNavBar /\u003e\n  \u003cHeroSection /\u003e\n  {genres \u0026\u0026 (\n    \u003cdiv className=\"container\"\u003e\n      {Object.values(genres).map((genre) =\u003e (\n        \u003cSection key={genre.value} genre={genre.value} /\u003e\n      ))}\n    \u003c/div\u003e\n  )}\n  \u003cdiv\n    className=\"page-end\"\n    onMouseEnter={() =\u003e {\n      setRequestedPage( np =\u003e np + 1 )\n    }}\n  /\u003e\n\u003c/\u003e\n```\n\nThe `\u003cSection\u003e` component works in the same way, but you will fully replace the data in the `movies` variable this time.\n\n```javascript\nconst fetchData = async () =\u003e {\n  const response = await fetch(\"/.netlify/functions/getMovies\", {\n    method: \"POST\",\n    body: JSON.stringify({ genre: genre, pageState: pageState }),\n  })\n  const responseBody = await response.json()\n  setMovies(responseBody.data.movies_by_genre.values)\n  setPageState(responseBody.data.movies_by_genre.pageState)\n}\n```\n\nNow that you know how the front-end works, launch the app!\n\n\u003c/details\u003e\n\n## 6. Install the Netlify CLI\n\nIn the `workshop-graphql-netflix` directory, run the following:\n\n```\nnpm install -g netlify-cli\n```\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Install Netlify CLI](images/netlify-install-cli.png)\n\n\u003c/details\u003e\n\nWith the Netlify command-line interface you will build and deploy\nthe application directly from the Gitpod terminal.\n\n## 7. Provide DB connection parameters\n\nThe \"serverless functions\" part of your app, in order to speak to\nyour DB through GraphQL, needs two important pieces of information:\nthe API endpoint and the token. You will now create a `.env` file which\ndefines them as environment variables.\n\nThe quickest way is to have `astra-cli` generate one for you:\n\n```\nastra db create-dotenv -k netflix workshops\n```\n\n\u003cdetails\u003e\u003csummary\u003eI want to do it manually\u003c/summary\u003e\n\nIf for some reason you don't use `astra-cli`, follow these steps:\n\n- copy `cp .env.sample .env` and open it: `gp open .env`;\n- `.env` is now open in the IDE editor and has two placeholders to replace:\n- insert the `AstraCS:...` database token (keep the quotes);\n- insert the GraphQL API address (it will look something like `https://b2f[...]/graphql/netflix`).\n\nYou can generate a new database token if you want. The GraphQL address\ncan be found in the playground: it is the URL you have edited to end in `netflix`\nin the second playground tab ([Part 1, step 4a](#4-insert-genre-data-with-graphql)).\n\n\u003c/details\u003e\n\n## 8. Run the app in dev mode\n\n✅ **Step 8a: Install dependencies:**\n\n```bash\nnpm install\n```\n\n✅ **Step 8b: Start the app:** With the command\n\n```\nnetlify dev\n```\n\nthe application should automatically be displayed in GitPod's \"simple browser\".\nNote that in this **dev-mode run** everything is local to your Gitpod instance:\n_the \"serverless functions\", in particular, are actually running there,\nalongside the rest of the application!_\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![run-in-preview-pane](images/preview.png)\n\n\u003c/details\u003e\n\nYou can copy the URL found in Gitpod's simple browser and open in a new tab\n(of your real browser, that is) for a\nbetter experience. But now it's time to move to the actual deploy phase.\n\n## 9. Connect to your Netlify site\n\n✅ **Step 9a:** Stop the dev run with `Ctrl-C`.\n\n✅ **Step 9b:** Authenticate with Netlify: run\n\n```\nnetlify login\n```\n\nthen grab the URL printed on the console\n(something like `https://app.netlify.com/authorize?response[...]`)\nand manually **open it in a new tab** (Gitpod blocks it for security).\nYou will be asked to authorize \"netlify-cli\" to access your Netlify account\nin the process.\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Netlify login](images/waiting_for_authorization-2.png)\n\n\u003c/details\u003e\n\nOnce you complete the login, you will see a console output like this:\n\n![Netlify login](images/netlify-login-2.png)\n\n✅ **Step 9c:** Associate to your Netlify site: run\n\n```\nnetlify link\n```\n\nand make sure you confirm the choice of associating to\n\"current git remote origin\".\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Netlify link](images/netlify-link-2.png)\n\n\u003c/details\u003e\n\n## 10. Deploy in production!\n\n✅ **Step 10a:** Inject secrets to the Netlify site\n\n```\nnetlify env:import .env\n```\n\nNow the (actually) serverless functions in Netlify have the connection\nparameters they need.\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n_Note: If you generated the `.env` with\n`astra-cli`, the actual output is much more verbose._\n\n![image](images/netlify_env_import.png)\n\n\u003c/details\u003e\n\n✅ **Step 10b:** Build the app\n\nRun\n\n```\nnetlify build\n```\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Netlify build](images/netlify-build.png)\n\n\u003c/details\u003e\n\n✅ **Step 10c:** Deploy!\n\n```\nnetlify deploy --prod\n```\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Netlify deploy in prod](images/netlify-deploy-prod-2.png)\n\n\u003c/details\u003e\n\n✅ **Step 10d:** Visit your site.\n\n```\nnetlify open:site\n```\n\n\u003cdetails\u003e\u003csummary\u003eShow me!\u003c/summary\u003e\n\n![Netlify Open site](images/netlify-open-site-2.png)\n\n\u003c/details\u003e\n\nIf needed, manually copy-paste your site URL in a new browser tab... and enjoy\nyour work!\n\n![Netlify Setup Example](images/deployed_netflix_clone.png)\n\n## The END\n\nCongratulations, you made it!\n\nNow don't forget to complete your assignment and [submit it](#homework)\nto get your badge of completion!\n\n```\n       ██╗    ██╗███████╗██╗     ██╗          \n       ██║    ██║██╔════╝██║     ██║          \n       ██║ █╗ ██║█████╗  ██║     ██║          \n       ██║███╗██║██╔══╝  ██║     ██║          \n       ╚███╔███╔╝███████╗███████╗███████╗     \n        ╚══╝╚══╝ ╚══════╝╚══════╝╚══════╝     \n                                              \n       ██████╗  ██████╗ ███╗   ██╗███████╗██╗ \n       ██╔══██╗██╔═══██╗████╗  ██║██╔════╝██║ \n       ██║  ██║██║   ██║██╔██╗ ██║█████╗  ██║ \n       ██║  ██║██║   ██║██║╚██╗██║██╔══╝  ╚═╝ \n       ██████╔╝╚██████╔╝██║ ╚████║███████╗██╗ \n       ╚═════╝  ╚═════╝ ╚═╝  ╚═══╝╚══════╝╚═╝ \n```\n\n## Homework\n\n\u003cimg src=\"images/netflix-badge.png?raw=true\" width=\"200\" align=\"right\" /\u003e\n\n🎓 Complete your upgrade and get your verified skill badge! Do the assignment and submit your homework!\n\n1. Complete the practice steps from this repository as described below.\n2. Insert a movie OR genre of your choice in the database (It's OK to re-use the trailer file URL from another movie! Just make the title recognizable as yours).\n3. Take a screenshot of your Netflix clone running either from your Gitpod or (better) deployed to production in Netlify (in this case, you could also give us the Netlify URL).\n4. The screenshot should clearly show the movie/genre you added (make sure you tell us its name when submitting).\n5. (Optional for extra wisdom) Watch the 2-hour video by Ania [HERE](#video-tutorial-with-ania-kubow), build the app yourself, and show us the running final result.\n6. Submit your homework [here](https://dtsx.io/homework-graphql-netflix).\n\nThat's it, you are done: expect an email in the next days!\n\n# Extra resources\n\n## Video tutorial with Ania Kubow\nThank you to our wonderful friend Ania Kubow for producing the Netflix clone. If you are not aware of Ania and love learning about coding you should absolutely check out her YouTube channel listed below.\n\nWhile we focused on getting you up and running to production with Astra DB and Netlify, Ania's video will dig into more details on the app itself. Check it out to dig in more.\n\n[Ania's Netflix Video](https://www.youtube.com/watch?v=g8COh40v2jU)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatastaxdevs%2Fworkshop-graphql-netflix","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fdatastaxdevs%2Fworkshop-graphql-netflix","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fdatastaxdevs%2Fworkshop-graphql-netflix/lists"}