Back to all courses
In this video I show you how to make a Word Association Game using an API. This project is great for those looking for fun projects to make with unique APIs.
My name is Ania Kubow and I am a Software Developer (ex-Eurostar) who quit the rat race to focus on my channel and teach others how to code by building retro Games and awesome projects. I am also part of the core team @FreeCodeCamp. You can find me putting out videos on there, as well as on my own channel every Wednesday (and sometimes an extra one during the week too)!
This guide will teach you how to build a word association game using an API from Rapid API. This API-based game will be a good addition to developers' skill sets and portfolios.
We will build a game that allows us to choose a level, fetch data based on that level, and map it onto cards and buttons. We will then build out the logic of getting a correct answer to show the word ‘correct’ under the button. It will give us a point and remove a point if we get the answer wrong.
As it is a beginner project using APIs and React, you should have a basic understanding of HTML, CSS, and JavaScript. You will also need to be familiar with React. However, even if you don't have a firm grip on React, follow this tutorial, as pushing yourself is how the best learning is done. With that said, let’s start right away.
To find the API for our game, I will go to Rapid API Hub. It is a platform where you can find and connect to thousands of well-developed APIs.
I love using Rapid API as I can scroll through all the options of their APIs in a categorized way. And not only that, but once I have a Rapid API key, I can use the same key for all the APIs you see. So there is no need to sign up to various different platforms and manage hundreds of passwords and API keys.
The Word Quiz API on Rapid API Hub fits our game perfectly, so let’s use it.
Go ahead and subscribe to the API. Upon testing the endpoints, you can see this API returns an object that shows us the level and an array under the quiz list
. The quizlist
array consists of the following elements:
quiz
: Three question words.option
: Two options to choose from.correct
: The correct answer.For easy integration, RapidAPI Hub automatically generates snippets in multiple languages and options. We will copy and use the (Node.js) Axios one, as you can see below.
js
const axios = require('axios');const options = {method: 'GET',url: 'https://twinword-word-association-quiz.p.rapidapi.com/type1/',params: { level: '3', area: 'sat' },headers: {'X-RapidAPI-Key': 'your-api-key','X-RapidAPI-Host': 'twinword-word-association-quiz.p.rapidapi.com',},};axios.request(options).then(function (response) {console.log(response.data);}).catch(function (error) {console.error(error);});
The X-RapidAPI-key
field will have your secret API key.
Now launch the code editor of your choice to make a new React project. I'll use WebStorm, call my project “Word game” and click on create.
WebStorm will spin up our React app containing different files. The App.js
file is the one where most of the code will be done. To keep things simple, I will delete the extra files not needed for our project and clean up a little of the code in App.js
file as well.
You can run this app by pressing the “run” button WebStorm and opening it on the browser. That’s how it will look in the console lookout of your browser.
We will install the Axios package for calling the API. We can do that by running the command npm i axios
in our terminal, making sure we are in the correct directory.
This will also create a new dependency in the ‘package.json’ file showing Axios and its version. Next, we will import Axios in our App.js
simply by writing:
js
import axios from 'axios';
Now, I will create a getRandomwords
function and paste the Axios (Node.js) snippet (that we copied above) in it. So, once we select a level, this function will fetch the data of that selected level. Now, this is what our App.js
file should look like.
js
import axios from 'axios';const App = () => {const getRandomWords = () => {const options = {method: 'GET',url: 'https://twinword-word-association-quiz.p.rapidapi.com/type1/',params: { level: '3', area: 'sat' },headers: {'x-rapidapi-host': 'twinword-word-association-quiz.p.rapidapi.com','x-rapidapi-key': 'your-api-key',},};axios.request(options).then(response => {console.log(response.data);}).catch(error => {console.error(error);});};return <div className="App"></div>;};export default App;
We need to store the chosen levels in a state. Moreover, for us to be able to select levels in our game, we will need to create a select
element and add level numbers as its options.
js
import axios from 'axios';import { useState } from 'react';const App = () => {const [chosenLevel, setChosenLevel] = useState(null);const getRandomWords = () => {const options = {method: 'GET',url: 'https://twinword-word-association-quiz.p.rapidapi.com/type1/',params: { level: '3', area: 'sat' },headers: {'x-rapidapi-host': 'twinword-word-association-quiz.p.rapidapi.com','x-rapidapi-key': 'your-api-key',},};axios.request(options).then(response => {console.log(response.data);}).catch(error => {console.error(error);});};console.log(chosenLevel);return (<div className="App"><select name="levels" id="levels" value={chosenLevel} onChange={e => setChosenLevel(e.target.value)}><option value="1">Level 1</option><option value="2">Level 2</option><option value="3">Level 3</option></select></div>);};export default App;
I have added a console.log
to verify our chosenLevel
state. Select a level, and it should log the level in your console like this.
Let's move on. Now that we can select different levels, we would want to pass them into the getRandomWords
function. So, we will replace the hard coded value with chosenLevel
inside the params of our request.
For layout, we will add some headings and new divs for level selector and question area.
js
import axios from 'axios';import { useState } from 'react';const App = () => {const [chosenLevel, setChosenLevel] = useState(null);const getRandomWords = () => {const options = {method: 'GET',url: 'https://twinword-word-association-quiz.p.rapidapi.com/type1/',params: { level: chosenLevel, area: 'sat' },headers: {'x-rapidapi-host': 'twinword-word-association-quiz.p.rapidapi.com','x-rapidapi-key': 'your-api-key',},};axios.request(options).then(response => {console.log(response.data);}).catch(error => {console.error(error);});};console.log(chosenLevel);return (<div className="App"><div className="level-selector"><h1>Word Association App</h1><p>Select Your level to start</p><select name="levels" id="levels" value={chosenLevel} onChange={e => setChosenLevel(e.target.value)}><option value="1">Level 1</option><option value="2">Level 2</option><option value="3">Level 3</option></select></div><div className="question-area"><h1>Welcome to level: {chosenLevel}</h1></div></div>);};export default App;
To make it look more pleasing to the eyes, let's add some colors to our headings in our index.css
file.
css
.level-selector {background-color: red;}.question-area {background-color: blue;}
Here is how it's going to look.
Now, on selecting a level, I want the red div to disappear so that we only see the blue one. At the moment, that's not happening. For that, I will use the chosenLevel
variable like this:
js
<div className="App">{ !chosenLevel && <div className="level-selector">
So, we have decided that our chosen level will be the reason that one div shows as opposed to another. We also want this chosen level to decide when we get our data. We can do it by using the ‘useEffect’ hook.
The ‘useEffect’ hook is essentially something that happens after the app renders, like a side effect. Also, we can make it re-render as many times as we wish based on certain variables.
Let’s create a hook that will run when the chosenLevel
changes. Inside the hook, we will check if the variable chosenLevel
exists. If it does, we will call the getRandomWords
function. In code, it translates to:
js
useEffect(() => {if (chosenLevel) getRandomWords();}, [chosenLevel]);
Now, let’s map our questions on little cards. For this, I will make a new div of ‘question-box’ and style it in the css file. You may make a card component for this and add styling and extra functionality to your cards. For this guide, I am going to keep it simple.
css
question-box {background-color: antiquewhite;padding: 10px;border-radius: 20px;margin: 10px;}
Great! Now that we have one little card. We want ten cards because that is the number of questions we have. We will map out the questions, options, and the correct answer on those cards.
js
import axios from 'axios';import { useState, useEffect } from 'react';const App = () => {const [chosenLevel, setChosenLevel] = useState(null);const [words, setWords] = useState(null);const getRandomWords = () => {const options = {method: 'GET',url: 'https://twinword-word-association-quiz.p.rapidapi.com/type1/',params: { level: chosenLevel, area: 'sat' },headers: {'x-rapidapi-host': 'twinword-word-association-quiz.p.rapidapi.com','x-rapidapi-key': 'your-api-key',},};axios.request(options).then(response => {// Storing API response (words) in the statesetWords(response.data);}).catch(error => {console.error(error);});};useEffect(() => {if (chosenLevel) getRandomWords();}, [chosenLevel]);return (<div className="App">{!chosenLevel && (<div className="level-selector"><h1>Word Association App</h1><p>Select Your level to start</p><selectname="levels"id="levels"value={chosenLevel}onChange={e => setChosenLevel(e.target.value)}><option value="1">Level 1</option><option value="2">Level 2</option><option value="3">Level 3</option></select></div>)}{chosenLevel && words && (<div className="question-area"><h1>Welcome to level: {chosenLevel}</h1>{words.quizist.map(question => (<div className="question-box"><p>{question.correct}</p></div>))}</div>)}</div>);};export default App;
Our app will now look like this:
Alright. As you can see, it is only showing the correct answers. We will need to map another array inside the question-box
div, to show the quiz words and the options of the quiz.
js
<div className="question-box">{question.quiz.map(tip => (<p>{tip}</p>))}<div className="question-buttons">{question.option.map(option => (<div className="question-button"><button>{option}</button></div>))}</div><p>{question.correct}</p></div>;
Let's have a look.
Now let’s make the question buttons appear next to each other.
css
.question-buttons {display: flex;flex-direction: row;}
I would also want the word “Correct!” to appear under the button if we get the correct answer. The question-button
will do it for us. I will use flex again, but this time I'm going to give it the flex direction of column
.
css
.question-button {display: flex;flex-direction: column;}
To check for the answer, I will write a function to check if our answer is correct based on the button we click. First, create the button:
js
<div className="question-button"><button onClick={() => checkAnswer(option, optionIndex + 1, question.correct)}>{option}</button></div>;
I will also need to give each of the options an index. We’ll have to add 1
to the index because our correct answers start with index of 1
. Now, if the option index equals the correct answer index, it will be considered the correct answer.
Here is the checkAnswer
function:
js
const checkAnswer = (option, optionIndex, correctAnswer) => {console.log(optionIndex, correctAnswer);if (optionIndex == correctAnswer) {setCorrectAnswers([...correctAnswers, option]);}};
When I click on the correct answer, it will add it to the correctAnswers
array. correctAnswers
is a state I have initialised like this:
js
const [correctAnswers, setCorrectAnswers] = useState([]);
The next thing that I want to do is simply display “Correct” if the answer is right. This will be easy because we're already collecting the correct answers in our array. We will simply add this function under the ‘question-button’.
js
<div className="question-button"><button onClick={() => checkAnswer(option, optionIndex + 1, question.correct)}>{option}</button>{correctAnswers.includes(option) && <p>Correct!</p>}</div>;
One last thing we need to do is add the score so that by the end, we know our final score. I will do this in the checkAnswer
function. Every correct answer will increase the existing score.
js
const [score, setScore] = useState(0);const checkAnswer = (option, optionIndex, correctAnswer) => {if (optionIndex == correctAnswer) {setCorrectAnswers([...correctAnswers, option]);setScore(score => score + 1);} else {setScore(score => score - 1);}};
The correct answer should be a secret, so we can hide it by removing the selected code:
We can also increase the number of levels of our game from three to ten simply by copying and pasting.
To make our app eye-catching, I will add the following css styles to the question area and buttons. I will keep it simple, but you can, of course, go wild and style it up as much as you want.
css
.app {font-family: 'Trebuchet MS', 'Lucinda Sans Unicode', 'Lucinda Grande';display: flex;justify-content: center;text-align: center;padding-top: 50px;}.question-area {max-width: 1000px;}.question-box {background-color: antiquewhite;padding: 10px;border-radius: 20px;margin: 10px;}.question-buttons {display: flex;flex-direction: row;}.question-button {display: flex;flex-direction: column;}button {margin: 5px;padding: 8px;border-radius: 5px;border: none;background-color: darkcyan;color: white;}button:disabled {background-color: darkgrey;color: darkolivegreen;}select {margin: 5px;padding: 8px;border-radius: 5px;border: none;background-color: darkcyan;color: white;}
That’s it! Our game is ready.
I hope you enjoyed building this game with me. You can create your own similar games using more APIs from RapidAPI.