Saturday, May 14, 2022
HomeWebsite DesignUse the Notion API to Create a Quiz with JavaScript

Use the Notion API to Create a Quiz with JavaScript


Notion is a multi-featured app for organizing all types of content material, from notes to calendars and reminders. In our final article, “Getting Began with the Notion API and Its JavaScript SDK”, we delved into the way to use Notion’s API and created a small interface to work together with it. This text now will discover one other use case for the Notion API: making a JavaScript quiz.

Whereas no earlier data is required to observe together with this text (I’ll present all of the required steps), we’ll be coping with front-end and back-end code, as there’s a little bit of Node.js and Specific setup concerned, so some JavaScript expertise are assumed.

JavaScript Quiz Venture Setup

We’re going to separate our setup into two sections. Within the first one, we’ll stroll by way of the required setup on the Notion facet, and one the second half we’ll work with our code.

To observe alongside, you’ll want a Notion account (extra on that beneath), in addition to a current copy of Node put in in your machine. As ever, the last code for the tutorial may be discovered on GitHub.

The Notion Setup

In the event you don’t have already got a Notion account, please create one by following this hyperlink. After creating your account and logging in, create a brand new web page by selecting Add a web page and provides it a reputation. For this tutorial, we’ll use a Desk database. Though it’s not the best database to construct a quiz, it’s the closest we are able to obtain with Notion!

Inserting info into the desk

Now that we’ve got our empty Desk, we have to work out the way to correctly insert our info into it.
Our meant schema for our quiz is the next:

{
  "1": {
    "Query": "Which is the aim of JavaScript?",
    "Solutions": {
      "1": "To model HTML Pages",
      "2": "So as to add interactivity to HTML pages",
      "3": "To carry out server facet scripting operations"
    },
    "Right": "So as to add interactivy to HTML pages"
  },
  "2": {
    "Query": "To insert a JavaScript into an HTML web page, which tag is used?",
    "Solutions": {
      "1": "<script="java">",
      "2": "<javascript>",
      "3": "<script>"
    },
    "Right": "<script>"
  },
  "3": {
    "Query": "Which of the next is right to put in writing “Hi there World” on the net web page?",
    "Solutions": {
      "1": "print('Hi there World')",
      "2": "doc.write('Hi there World')",
      "3": "response.write('Hi there World')"
    },
    "Right": "doc.write('Hi there World')"
  }
}

Notion isn’t actually constructed for the sort of database, so we have to get inventive with it! 💡 So our Query column will probably be only a Title (which works nice) and our Right column will probably be of kind Textual content (which additionally works as meant). For our a number of choices, although, we have to do one thing completely different! My method to it’s to make use of the Multi-select discipline kind so I can write a number of outcomes into the identical cell (we’ll see down the trail what the info retrieved from this seems to be like). So our Desk ought to appear to be the next picture as the top consequence.

Our Notion Table filled in with our questions, answers and correct choice

Creating our Notion API integration

Now we have to go to the Notion API web site to create our integration. Press the My Integrations button on the highest proper nook after which click on on Create new integration. We have to fill in our title and make sure to select our Related workspace (will probably be chosen by default however be certain that of this). If we press Submit, we’ll be directed to a brand new web page with an Inner Integration Token (we’ll use this in our code) and with two choice bins for the place we need to use our integration. We don’t have to do something on this web page apart from copy our token and press Save adjustments.

Now we return to our Notion workspace. In our newly created Notion database, we need to press Share, then Invite. We’ll then have the ability to select our newly created integration. If we select it and press Invite, our Notion setup is finished. Properly achieved! 🙌

The Code Setup

For our code setup, we’ll use a Notion template repository (which we are able to then use on any of our Notion API initiatives!) that already has the preliminary code required to work with Notion’s API. This repo may be discovered right here, so make sure to fork it and clone it to accompany our challenge! In the event you’re taken with what all of the bits of code do, please discuss with “Getting Began with the Notion API and Its JavaScript SDK”, since we go into quite a lot of element for each little bit of code (the repo additionally has a README that ought to clarify what it does!).

Putting in dependencies

After forking and cloning our Notion template repo, our first step is to put in our dependencies, so be certain that to run yarn or npm set up on our terminal contained in the folder. On this challenge we’ve got three dependencies: the @notionhq/shopper, dotenv and Specific. We’ll use dotenv to handle our variables and maintain them protected (such because the one we noticed on our integration) and Specific to create our again finish and server for our utility.

To handle our variables with dotenv, create a .env file on the root of our challenge and paste the next there:

NOTION_API_KEY = YOUR_TOKEN_HERE
NOTION_API_DATABASE = YOUR_DATABASE_ID_HERE

Our NOTION_API_KEY is the important thing that we are able to discover in our Integrations, and our NOTION_API_DATABASE may be discovered by going to the created Notion web page and searching on the browser’s URL bar. It comes after your workspace identify (if we’ve got one) and the slash (myworkspace/) and earlier than the query mark (?). The ID is 32 characters lengthy, containing numbers and letters:

https://www.notion.so/myworkspace/a8aec43384f447ed84390e8e42c2e089?v=...
                                  |--------- Database ID --------|

If we’re fearful about having Notion’s API keys on a file inside our repo, discover that on our .gitignore we’ve got the .env file there; the .gitignore permits us to place completely different file/folder names inside, and that implies that these information/folders gained’t be added to our repo after we push our code.

Now that we’ve got our preliminary repository and we’ve got the required credentials from Notion, we are able to begin engaged on our quiz!

Grabbing the JavaScript Quiz Knowledge

We should always first verify if we’re efficiently related to our Notion database, so we’ll navigate to our .index.js file and log our reponse variable (see how we’re grabbing our databaseId from our .env file and utilizing it on our database question?).

If we then run yarn begin, we should always see one thing like the next screenshot on our terminal.

What logging the response query retrieves

Seeing this on our terminal implies that we’re correctly related to our Notion database and that we are able to now get our required information. Our getDatabase operate will appear to be this:

exports.getDatabase = async operate () {
  const response = await notion.databases.question({ database_id: databaseId });

  const responseResults = response.outcomes.map((web page) => {
    return {
      id: web page.id,
      query: web page.properties.Query.title[0].plain_text,
      solutions: web page.properties.Solutions.multi_select,
      right: web page.properties.Right.rich_text[0].plain_text,
    };
  });

  return responseResults;
};

With responseResults, we’re mapping over our outcomes (matching the entries in our database) and we’re mapping the paths for various properties to names that we’re selecting (on this case, id, query, solutions and right). Discover how particular the article path is. That is by design, which implies that, when creating and dealing with your individual database, it’s best to repeatedly examine the returned properties till you discover the knowledge you’re in search of (it’s actually a matter of trial and error).

With this new code, we’re just about making a name to our API and selecting the properties that we need to use in our code, which implies that we’re able to work with them on our interface!

Exhibiting Our Knowledge within the Browser

Let’s begin by coping with our HTML and CSS, since they’re fairly direct! We gained’t do any adjustments on our HTML from the template, and on our model.css we are able to paste the next code beneath the present one:

.questionContainer {
  padding: 30px;

  show: flex;
  flex-direction: column;
  justify-content: heart;
  align-items: heart;

  box-shadow: rgba(149, 157, 165, 0.2) 0px 8px 24px;
  border-radius: 10px;
}

.numberElement {
  margin: 0px auto 10px;

  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
}

.query {
  margin: 0px auto 40px;
}

.answersDiv {
  width: 100%;

  show: flex;
  flex-direction: column;
  hole: 20px;
}

.answerOption {
  padding: 20px;
  margin: 0px;

  font-size: 18px;
  text-align: heart;

  cursor: pointer;

  border: 1px strong rgb(42, 43, 44);
  border-radius: 40px;
}

We gained’t see these styling adjustments but, however we are able to now concentrate on the performance, which is the extra necessary a part of this tutorial.

If we now go to the primary.js file contained in the public folder, we’ll see that we’re already grabbing our information from our again finish with the getDataFromBackend operate. In the event you’re confused by this, “Getting Began with the Notion API and Its JavaScript SDK” has a lengthier clarification for it, however mainly on our server.js we created a route that will get our database, and right here on getDataFromBackend we’re making a fetch to that very same route, which is able to seize our information for us.

Discover how inside addData we have already got const information = await getDataFromBackend();. Which means we’re prepared to start out working with our information, and we are able to really verify this! log this information variable and we should always see, on our console, an array of our database entries.

Logging our data

Exhibiting our information on the display screen

Now that we all know what our returned information seems to be like, we have to consider how we really need to present it on the display screen. My thought for it’s to have a card for every query, with the completely different solutions inside, and when the person presses the proper reply, the reply’s background ought to flip inexperienced; if it’s unsuitable, it ought to flip purple.

Let’s begin by making a <div> for every query. We are able to do that by iterating over our array with a forEach loop, permitting us to create the <div> and add some lessons to it.

So inside our addData operate we are able to do that:

const addData = async () => {
  const information = await getDataFromBackend();

  information.forEach((worth, index) => {
    const div = doc.createElement('div');
    div.classList.add('questionContainer');

    container.append(div);
  });
};

But it surely nonetheless feels a bit empty, so let’s append a header for every card like this:

const addData = async () => {
  const information = await getDataFromBackend();

  information.forEach((worth, index) => {
    const div = doc.createElement('div');
    div.classList.add('questionContainer');

    const numberElement = doc.createElement('p');
    numberElement.classList.add('numberElement');
    numberElement.innerHTML = `Query ${index + 1}`;

    div.appendChild(numberElement);

    container.append(div);
  });
};

Right here we’re making a <p> and giving it a category, and we’re working with index + 1, as a result of arrays in JavaScript are zero-based and we don’t need to see Query 0, since that doesn’t make sense! If we now begin our app, we should always see one thing like what’s pictured beneath.

We're now rendering a card for each question and displaying a header for it that reads Question ${index + 1}

The enjoyable half: new capabilities for rendering questions and solutions

Now for the enjoyable half! We may do all of our logic inside addData, however that may get too messy, so we’re going to create new capabilities to render our query and solutions.

Let’s begin with the query, and let’s do some work on our addData operate that gained’t nonetheless translate into a lot:

const addData = async () => {
  const information = await getDataFromBackend();

  information.forEach((worth, index) => {
    const div = doc.createElement('div');
    div.classList.add('questionContainer');

    const numberElement = doc.createElement('p');
    numberElement.classList.add('numberElement');
    numberElement.innerHTML = `Query ${index + 1}`;

    div.appendChild(numberElement);

    
    const query = createQuestion(worth.query);

    div.appendChild(query);
    

    container.append(div);
  });
};

The code we simply added is similar to that for the numberElement, however right here we’re assigning a operate to a variable and appending that variable. Discover additionally that we’re passing worth.query into our createQuestion, as a result of we need to work with and render the query, in fact. It would all make sense very quickly — don’t fear!

Now, outdoors and above addData, let’s create this new createQuestion operate. Inside it, we just about need the identical logic that we added for our numberElement: create a component, give it a category, and add some content material to it. Right here we’ll be utilizing not innerHTML however createTextNode: since our questions are associated with code, if we have been to make use of innerHTML in one thing like <b>textual content</b>, it could really render the phrase textual content however with daring as a substitute of the whole syntax (you’ll be able to see an instance right here). Our last createQuestion will appear to be this:

const createQuestion = (query) => {
  const questionElement = doc.createElement('h3');
  questionElement.classList.add('query');
  const questionNode = doc.createTextNode(query);

  questionElement.appendChild(questionNode);

  return questionElement;
};

If we now run yarn begin, our browser ought to seem as proven beneath.

We should now see the question card with the question header and the newly added question

Now our setup for our solutions is just about the identical. Let’s first do the identical factor that we did with createQuestion inside addData:

const addData = async () => {
  const information = await getDataFromBackend();

  information.forEach((worth, index) => {
    const div = doc.createElement('div');
    div.classList.add('questionContainer');

    const numberElement = doc.createElement('p');
    numberElement.classList.add('numberElement');
    numberElement.innerHTML = `Query ${index + 1}`;

    div.appendChild(numberElement);

    const query = createQuestion(worth.query);

    div.appendChild(query);

    
    const solutions = createAnswers(worth);

    div.appendChild(solutions);
    

    container.append(div);
  });
};

And now our preliminary search for createAnswers will appear to be this:

const createAnswers = (worth) => {
  const answersDiv = doc.createElement('div');
  answersDiv.classList.add('answersDiv');

  return answersDiv;
};

Discover how we’re doing const solutions = createAnswers(worth);. We are able to’t simply move worth.solutions to our operate, as a result of we additionally want the worth.right. We may as a substitute of this move two arguments to our operate: one for the array of solutions and the opposite one can be the right one.

Rendering an array of solutions

We now have an array of solutions, and we have to render all of them, so we’d like a loop to undergo all of them. The method inside this loop will just about be the identical as for all the opposite parts, so we needs to be execs at this level at rendering parts on the DOM:

const createAnswers = (worth) => {
  const answersDiv = doc.createElement('div');
  answersDiv.classList.add('answersDiv');

  for (let i = 0; i < worth.solutions.size; i++) {
    const answerElement = doc.createElement('p');
    answerElement.classList.add('answerOption');
    const answerNode = doc.createTextNode(worth.solutions[i].identify);

    answerElement.appendChild(answerNode);

    answersDiv.appendChild(answerElement);
  }

  return answersDiv;
};

With this code, we’re looping over our array, creating a component, giving it a category and utilizing createTextNode to render our solutions. (Funnily sufficient, if we had used innerHTML right here, the solutions with <script> wouldn’t render 😄.) Then we’re merely appending this answerNode to our <p> and appending this one to our <div>! If we run yarn begin, we’ll now see our total quiz! Woo! 🚀

A partial view of our quiz with all the required elements in it

Interacting with the Solutions

Hmm… However we are able to’t actually work together with the solutions, and it’s probably not a quiz if we don’t get to know if we acquired the reply proper or unsuitable, proper? We should always repair that!

We all know that we need to click on on every reply and know if it’s proper or unsuitable, so we are able to begin by including an occasion listener to it like so:

const createAnswers = (worth) => {
  const answersDiv = doc.createElement('div');
  answersDiv.classList.add('answersDiv');

  for (let i = 0; i < worth.solutions.size; i++) {
    const answerElement = doc.createElement('p');
    answerElement.classList.add('answerOption');
    const answerNode = doc.createTextNode(worth.solutions[i].identify);

    
    answerElement.addEventListener('click on', () => {});
    

    answerElement.appendChild(answerNode);

    answersDiv.appendChild(answerElement);
  }

  return answersDiv;
};

Keep in mind that we did createAnswers(worth), so we may get the worth.right? Now it’s its time to shine! Once we click on on a solution, there are two attainable outcomes: the person chooses the reply that’s equal to the proper reply, or the person chooses a solution that’s not equal to the proper reply. To take care of these attainable outcomes, we’re going to make use of an if assertion, and the best way we’re going to point out to our customers that they acquired a solution proper or unsuitable is thru a change of the background-color of the reply. So our logic will appear to be this:

const createAnswers = (worth) => {
  const answersDiv = doc.createElement('div');
  answersDiv.classList.add('answersDiv');

  for (let i = 0; i < worth.solutions.size; i++) {
    const answerElement = doc.createElement('p');
    answerElement.classList.add('answerOption');
    const answerNode = doc.createTextNode(worth.solutions[i].identify);

    answerElement.addEventListener('click on', () => {
      
      answerElement.model.shade = 'white';
      if (worth.solutions[i].identify !== worth.right) {
        
        answerElement.model.backgroundColor = '#f55142';
      } else {
        
        answerElement.model.backgroundColor = '#6dbf39';
      }
      
    });

    answerElement.appendChild(answerNode);

    answersDiv.appendChild(answerElement);
  }

  return answersDiv;
};

So with every click on, we’re altering the textual content shade to white, after which we’re checking if the identify property of every reply is the same as worth.right (that is clearly not preferrred, and an index can be quite a bit higher, however we did our greatest with Notion’s databases!). If it’s not, we modify its shade to purple and whether it is, we modify it to inexperienced!

Our quiz is now complete with full interaction for right and wrong answers

And that’s our quiz achieved! 🚀 Isn’t it implausible?

Notion JavaScript Quiz Wrapup

On this tutorial, we’ve explored quite a lot of functionalities offered by Notion’s API and, actually, it’s all the time very thrilling to see how a lot you are able to do with such a straightforward device!

I hope this put up finally ends up inspiring you to discover the Notion API and to construct your individual quizzes and different rad stuff with Notion!

If you wish to shortly take a look at this Notion challenge, you’ll be able to clone it from our GitHub repo.

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

Most Popular

Recent Comments