50 minutes, Intermediate, Start Building
A simple ReactJS Netflix homepage clone running on Astra DB that leverages the GraphQL API with paging and infinite scrolling. This application is the result of the collaboration between Ania Kubow and the Datastax Developer Advocate team.
See the Video Demo of what you will build!
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:
astra-cli
đ Complete the assignment, receive your Badge!
đ When creating your instance, use the promotion code ANIA200 to get 200$ of additional free credit!
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.
Leveraging Database creation guide create a database. Right-Click the following button and Open in a new TAB.
Field | Value |
---|---|
Database Name | workshops |
Keyspace Name | netflix |
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. |
âšī¸ Note: If you already have a database
workshops
, simply add a keyspacenetflix
using theAdd 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.
While the database is being created, you will also get a Security token (needed to authenticate with your database and start using it): please IGNORE THIS ONE, as we will be soon creating a new, more powerful token for today.
The status will change from Pending
to Active
when the database is ready, this usually only takes 2-3 minutes.
Note: this step is very important, as the token generated automatically for you with the database lacks some permissions we'll use in the workshop.
Create a token for your app, using the "Database Administrator" role.
Keep it handy for later use (best to download it in CSV format, as the values
will not be visible afterward).
This will provide authentication later when interacting with the database.
Today, in particular, you'll need the string labeled "token" (the one starting with AstraCS:...
).
â ī¸ Important The instructor will show the token creation on screen, but will then destroy it immediately for security reasons.
â Step 3a: Open GraphQL Playground:
Connect
TABAPIs
connection methodGraphQL API
is selectedNote: in the following, we will refer to "playground tabs". These are not the tabs in your browser, rather they are tabs within the Playground application, to switch between the (logically distinct) realms of "managing schema" and "managing data in the tables" (more on that later).
â Step 3b: Provide the database token as header
In the GraphQL Playground, Populate HTTP HEADER variable x-cassandra-token
on the bottom of the page with your token (including the AstraCS:
part).
This is the "Database Administrator" token you created earlier on the Astra DB dashboard (Step 2 above).
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 to be identical to those that the Netlify functions will run from code, so please replace the token with your DB token as instructed.
â
Step 3c: In GraphQL Playground, create the reference_list
table:
Copy the following mutation to the left panel
mutation createReferenceList {
reference_list: createTable(
keyspaceName:"netflix",
tableName:"reference_list",
ifNotExists:true
partitionKeys: [
{ name: "label", type: {basic: TEXT} }
]
clusteringKeys: [
{ name: "value", type: {basic: TEXT}, order: "ASC" }
]
)
}
GraphQL Playground troubleshooting (covers this whole section)
Trouble | Shooting |
---|---|
Server cannot be reached | Add Astra token to headers (including AstraCS:... ; check quotes) |
Server cannot be reached (second playground tab) | Check playground target URL ends with netflix |
Response not successful: Received status code 401 | Same as "server cannot be reached" |
Response not successful: Received status code 404 | Check spelling of keyspace in target URL |
"Play" button does nothing | Ensure query is syntactically correct |
"Validation error of type FieldUndefined" | Most likely query in the wrong playground tab, or writing to table not created yet |
â Step 4a: Get to the API URL for your keyspace
In graphQL playground, change playground tab to now use graphql
. The Playground has its own address bar
(note: it's not the address bar of your browser). Edit the ending of the URL shown there, from system
to the
name of the keyspace: netflix
â
Step 4b: Repeat the insertion of the x-cassandra-token
header for this playground tab (as you did for the first one):
â Step 4c: In the GraphQL Playground, run the mutation that writes genre data:
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}
}
classic: insertreference_list(value: {label:"genre", value:"Classic"}) {
value{value}
}
comedies: insertreference_list(value: {label:"genre", value:"Comedies"}) {
value{value}
}
crime: insertreference_list(value: {label:"genre", value:"Crime"}) {
value{value}
}
cult: insertreference_list(value: {label:"genre", value:"Cult"}) {
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}
}
international: insertreference_list(value: {label:"genre", value:"International"}) {
value{value}
}
italian: insertreference_list(value: {label:"genre", value:"Italian"}) {
value{value}
}
musicmusicals: insertreference_list(value: {label:"genre", value:"Music & Musicals"}) {
value{value}
}
realitytv: insertreference_list(value: {label:"genre", value:"Reality TV"}) {
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}
}
tvshow: insertreference_list(value: {label:"genre", value:"TV Show"}) {
value{value}
}
}
then click on the big "play button" arrow in the center to execute the mutation
â
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:
query getAllGenres {
reference_list (value: {label:"genre"}) {
values {
value
}
}
}
â Step 6a: Switch back to first playground tab ("graphql-schema"; the token header will be already set).
Use the following mutation to create a new table:
mutation createMoviesTable {
movies_by_genre: createTable(
keyspaceName:"netflix",
tableName:"movies_by_genre",
ifNotExists: true,
partitionKeys: [
{ name: "genre", type: {basic: TEXT} }
]
clusteringKeys: [
{ name: "year", type: {basic: INT}, order: "DESC" },
{ name: "title", type: {basic: TEXT}, order: "ASC" }
]
values: [
{ name: "synopsis", type: {basic: TEXT} },
{ name: "duration", type: {basic: INT} },
{ name: "thumbnail", type: {basic: TEXT} }
]
)
}
â Step 7a: Go to playground tab "graphql" again.
Use the following mutation to populate the movies_by_genre
table with four movies:
mutation insertMovies {
inception: insertmovies_by_genre(
value: {
genre:"Sci-Fi",
year:2010,
title:"Inception",
synopsis:"Cobb steals information from his targets by entering their dreams.",
duration:121,
thumbnail:"https://i.imgur.com/RPa4UdO.mp4"}) {
value{title}
}
prometheus: insertmovies_by_genre(value: {
genre:"Sci-Fi",
year:2012,
title:"Prometheus",
synopsis:"After a clue to mankind's origins is discovered, explorers are sent to the darkest corner of the universe.",
duration:134,
thumbnail:"https://i.imgur.com/L8k6Bau.mp4"}) {
value{title}
}
aliens: insertmovies_by_genre(value: {
genre:"Sci-Fi",
year:1986,
title:"Aliens",
synopsis:"Ellen Ripley is sent back to the planet LV-426 to establish contact with a terraforming colony.",
duration:134,
thumbnail:"https://i.imgur.com/QvkrnyZ.mp4"}) {
value{title}
}
bladeRunner: insertmovies_by_genre(value: {
genre:"Sci-Fi",
year:1982,
title:"Blade Runner",
synopsis:"Young Blade Runner K's discovery of a long-buried secret leads him to track down former Blade Runner Rick Deckard.",
duration:145,
thumbnail:"https://i.imgur.com/xhhvmj1.mp4"}) {
value{title}
}
}
â 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:
query getMovieAction {
movies_by_genre (
value: {genre:"Sci-Fi"},
orderBy: [year_DESC]
) {
values {
year,
title,
duration,
synopsis,
thumbnail
}
}
}
â 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:
query getMovieActionPag1 {
movies_by_genre (
value: {genre:"Sci-Fi"},
options: {pageSize: 2},
orderBy: [year_DESC]
) {
values {
year,
title,
duration,
synopsis,
thumbnail
}
pageState
}
}
â Step 8c: Fetch the next page:
Notice that pageState
now is also returned. Use it to fetch the next 2 items (next page):
edit the next query to replace YOUR_PAGE_STATE
with your own string value:
query getMovieActionNextPage {
movies_by_genre (
value: {genre:"Sci-Fi"},
options: {pageSize: 2, pageState: "YOUR_PAGE_STATE"},
orderBy: [year_DESC]
) {
values {
year,
title,
duration,
synopsis,
thumbnail
}
pageState
}
}
If 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:
this is how pagination signals the end of the full results list.
â 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.
Note: preferrably Ctrl-click for a new tab.
This will take a few minutes:
Note: if there is an existing account in Netlify, check the settings to make sure the Netlify account is connected to your Github account.
â
Step 1b: Check the deploy logs: Click on Site deploy in progress
within the Netlify UI.
Then click the top deploy link to see the build process.
â
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.
â 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.
â
Step 2a: Jump to YOUR repo: Click on the GitHub
in Deploys from GitHub
to get to your new repository on Github.
Scroll to where you were in the README.
Note At this point, you MUST be reading this README from YOUR Github repository. That is, if the address bar still says
https://github.com/datastaxdevs/...
please head over to YOUR copy of the repo before going the Gitpod route!
â Step 2b: Launch Gitpod:
Use this link to open Gitpod from YOUR repository! (Tip: Ctrl-click on the button to open in new tab.)
Note: the button works on Chrome and Firefox.
âšī¸ It may take a few minutes (approx. 3-5) for GitPod to fully initialize. Please wait until the console in the lower half of Gitpod is responsive.
You may see a dialog about "opening this workspace in VS Code Desktop": you can safely dismiss it.
Gitpod will be your IDE from now on. If you are familiar with VSCode, you can probably just use it. Otherwise, take a moment to review a separate page "Know your Gitpod" and then come back here.
astra-cli
You are going to use a CLI tool to simplify operations with Astra DB. The tool is preinstalled on your Gitpod.
â Step 3a: Set up the CLI:
Run the following in the Gitpod terminal and,
when prompted, enter the AstraCS:...
you obtained at the beginning.
astra setup
â
Step 3b: Bulk data load: Load a large movie dataset in the database.
This command installs and properly launches the DSBulk
tool (docs):
astra db load workshops \
-url data/movies_by_genre.csv \
-k netflix \
-t movies_by_genre
Note: we mock the trailers for these thousands of movies by using a handful of them over and over. Don't be surprised if you'll see the wrong trailers for your favorite movie!
That's it! All 6000+ movies are now loaded and ready to go!
Note: this section and the next one ("Fetching from the Front-End") are not steps to "perform", rather suggestions to dive in the app code and figure out how the various parts (React components, Netlify functions and finally the GraphQL server in Astra DB) fit together. You can skip these and jump to the next practical step if you are in a hurry, but please come back to these for reference if you want to dissect the code!
In the workshop-graphql-netflix
directory, run the following:
npm install -g netlify-cli
With the Netlify command-line interface you will build and deploy the application directly from the Gitpod terminal.
The "serverless functions" part of your app, in order to speak to
your DB through GraphQL, needs two important pieces of information:
the API endpoint and the token. You will now create a .env
file which
defines them as environment variables.
The quickest way is to have astra-cli
generate one for you:
astra db create-dotenv -k netflix workshops
â Step 8a: Install dependencies:
npm install
â Step 8b: Start the app: With the command
netlify dev
the application should automatically be displayed in GitPod's "simple browser". Note that in this dev-mode run everything is local to your Gitpod instance: the "serverless functions", in particular, are actually running there, alongside the rest of the application!
You can copy the URL found in Gitpod's simple browser and open in a new tab (of your real browser, that is) for a better experience. But now it's time to move to the actual deploy phase.
â
Step 9a: Stop the dev run with Ctrl-C
.
â Step 9b: Authenticate with Netlify: run
netlify login
then grab the URL printed on the console
(something like https://app.netlify.com/authorize?response[...]
)
and manually open it in a new tab (Gitpod blocks it for security).
You will be asked to authorize "netlify-cli" to access your Netlify account
in the process.
Once you complete the login, you will see a console output like this:
â Step 9c: Associate to your Netlify site: run
netlify link
and make sure you confirm the choice of associating to "current git remote origin".
â Step 10a: Inject secrets to the Netlify site
netlify env:import .env
Now the (actually) serverless functions in Netlify have the connection parameters they need.
â Step 10b: Build the app
Run
netlify build
â Step 10c: Deploy!
netlify deploy --prod
â Step 10d: Visit your site.
netlify open:site
If needed, manually copy-paste your site URL in a new browser tab... and enjoy your work!
Congratulations, you made it!
Now don't forget to complete your assignment and submit it to get your badge of completion!
âââ ââââââââââââââ âââ
âââ ââââââââââââââ âââ
âââ ââ âââââââââ âââ âââ
ââââââââââââââââ âââ âââ
ââââââââââââââââââââââââââââââââââ
ââââââââ ââââââââââââââââââââââââ
âââââââ âââââââ ââââ ââââââââââââââ
ââââââââââââââââââââââ ââââââââââââââ
âââ ââââââ âââââââââ âââââââââ âââ
âââ ââââââ âââââââââââââââââââ âââ
ââââââââââââââââââââ âââââââââââââââââ
âââââââ âââââââ âââ ââââââââââââââââ
đ Complete your upgrade and get your verified skill badge! Do the assignment and submit your homework!
That's it, you are done: expect an email in the next days!
Thank 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.
While 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.