How to Build a Forum App with NextJs and Strapi CMS

This page summarizes the projects mentioned and recommended in the original post on dev.to

Our great sponsors
  • SurveyJS - Open-Source JSON Form Builder to Create Dynamic Forms Right in Your App
  • WorkOS - The modern identity platform for B2B SaaS
  • InfluxDB - Power Real-Time Data Analytics at Scale
  • community-content

    Contribute and collaborate on educational content for the Strapi Community

  • While programming, programmers encounter various challenges, which make them solicit help with solving these problems. Forums provide a tech community of enthusiasts who can assist with these problems. We will be building a forum site with NextJs on the Front-end and Strapi for content management

  • Next.js

    The React Framework

  • We will build the front-end of our forum application with Next.js. Next.js is an open-source framework built on Node.js to allow React applications to be rendered on the server-side.

  • SurveyJS

    Open-Source JSON Form Builder to Create Dynamic Forms Right in Your App. With SurveyJS form UI libraries, you can build and style forms in a fully-integrated drag & drop form builder, render them in your JS app, and store form submission data in any backend, inc. PHP, ASP.NET Core, and Node.js.

    SurveyJS logo
  • Strapi-Forumapp

    Built a forum application with NextJs and Strapi

  • import React, { useState } from "react"; import style from "../../styles/Home.module.css"; import Link from "next/link"; function Displayforum() { const [show, setShow] = useState(false); return ( Display forum Ask a question Login Questions Posted By: Victory Tuduo Description of the Question Answers Post setShow(!show)}> {show ? "Hide Answers" : "Show Answers"} {show ? (

    Miracle

    Try doing it Like this

    ) : null} ); } export default Displayforum;
    Enter fullscreen mode Exit fullscreen mode

    This component handles the layout of our display forum page. We also have a button here that directs the user to the page to upload new questions.

    Meanwhile, in upload.js we have the following:

        import React from "react";
        import Uploadforum from "./Components/Uploadforum";
        function upload() {
          return (
            
    ); } export default upload;
    Enter fullscreen mode Exit fullscreen mode

    Here, we simply added an import for the Uploadforum component into our page. In Uploadforum.js file we have a simple form to create new questions:

        import React from "react";
        import style from "../../styles/Home.module.css";
        import Link from "next/Link";
        function Uploadforum() {
          return (
            

    Ask a question

    Forum
    <button>Submit Question</button> </form> </div> </div> ); } export default Uploadforum; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Finally, we have the following styles in <code>Home.module.css</code><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> .container { min-height: 100vh; padding: 0 0.5rem; height: 100vh; font-family: monospace; } /* display forum page */ .topcont { display: flex; justify-content: space-between; align-items: center; padding: 5px 8px; } .topcont button, .inputanswer button, .formcont button, .showanswer { border: none; color: #fff; background: dodgerblue; border-radius: 8px; padding: 10px 15px; outline: none; margin: 8px; } .topcont button:hover { cursor: pointer; transform: scale(1.2); } .heading { font-weight: bold; } .subheading { font-weight: 500; text-transform: uppercase; } .userinfo { font-size: 18px; font-weight: 600; } .questioncont { min-height: 300px; padding: 15px 14px; box-shadow: 12px 12px 36px rgba(0, 0, 0, 0.12); } .answercont { min-height: 300px; padding: 5px 3px 5px 15px; } .answers { height: 300px; overflow-x: scroll; } .inputanswer { margin-bottom: 8px; } .inputanswer textarea { width: 100%; resize: none; padding: 5px 8px; } .showanswer { border: 1px solid dodgerblue; background: #fff; color: dodgerblue; transition: 0.4s ease-in-out; } .showanswer:hover { background: dodgerblue; color: #fff; } .eachanswer { border-radius: 15px; background: #e7e7e7; padding: 8px 15px; margin-bottom: 10px; } .username { font-weight: bold; text-transform: uppercase; } .answertext { font-family: Montserrat; font-size: 14px; font-weight: 500; } /* upload a question page */ .uploadpage { min-height: 100vh; } .formcont { min-width: 100vw; display: flex; justify-content: center; align-items: center; } .uploadform { display: flex; flex-direction: column; min-width: 500px; padding-top: 10px; } .uploadform input, .uploadform textarea { resize: none; width: 100%; margin: 8px; padding: 5px; } </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>All of this makes up the layout of our pages.</p> <h2> <a name="getting-data-from-strapi" href="#getting-data-from-strapi"> </a> Getting data from Strapi: </h2> <h2> <a name="setting-up-our-fetch-request" href="#setting-up-our-fetch-request"> </a> Setting up our Fetch Request </h2> <p>In this section, we will fetch our data from Strapi and display it in our app. We will be using <a href="https://axios-http.com/docs/intro">Axios</a> to perform our fetch operations.</p> <p>We will install this via CLI:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code> npm <span class="nb">install </span>axios </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Create a file <code>index.js</code> in the API folder. Here, we will set up our fetch request:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import axios from "axios"; const url = "http://localhost:1337/strapi-forums"; export const readForum = () => axios.get(url); export const createQuestion = (newQuestion) => axios.post(url, newQuestion); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Above, we added import for <code>axios</code>, the URL to fetch our data, and exported functions to read and create data from our forum.</p> <p>We’ll import these functions into our app in our <code>index.js</code> file:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { readForum, createQuestion } from "./api"; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <h2> <a name="fetching-data-from-strapi" href="#fetching-data-from-strapi"> </a> Fetching Data from Strapi </h2> <p>We will fetch the data from Strapi in our <code>index.js</code> file and pass it to <code>Displayforum.js</code> component to display it:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { react, useState, useEffect } from "react"; ... const [question, setQuestions] = useState({}); const [response, setResponse] = useState([]); useEffect(() => { const fetchData = async () => { const result = await readForum(); setResponse(result.data); }; fetchData(); }, []); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Here, we fetched our data from Strapi and assigned it to <code>response</code> with the React <code>useState</code> hook. We have a <code>useEffect</code> function that makes the request when our component mounts.</p> <p>Now, we pass this <code>response</code> down to our <code>Displayforum</code> component.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <Displayforum response={response} /> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <h2> <a name="displaying-data-from-strap" href="#displaying-data-from-strap"> </a> Displaying Data from Strap </h2> <p>To display our data in our <code>Displayforum.js</code> file, we will map our responses and render our components. Before we do this, we will create two additional text fields in our Strapi CollectionCollection: <code>Username</code> and <code>Answername</code>.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c2XelJbV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635543765246_created%2Bnew%2Bfields.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c2XelJbV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635543765246_created%2Bnew%2Bfields.jpg" alt="" loading="lazy" width="792" height="468"></a></p> <p>Back in our <code>Displayforum</code> component we will proceed with displaying our data:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> ... function Displayforum({ response }) { ... {response.map((response, index) => ( <div key={index}> <div className={style.userinfo}> ... <p className={style.answertext}>Try doing it Like this</p> </div> </div> ) : null} </div> </div> ))} </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Here, we wrapped up our components to map through <code>response</code> and display this component as many times as the number of responses. To display our Strapi data, we simply reference it. We can get our <code>Username</code> with this code:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> response.Username </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We can now add this to our component and display it:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <p>Posted By: {response.Username}</p> ... <p className={style.question}>{response.Questions}</p> ... <p className={style.username}>{response.Answername}</p> <p className={style.answertext}>{response.Answers}</p> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We have successfully added the data from our CollectionCollection to our <code>front-end</code> to view this in the browser. Run the following command in the CLI:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code> npm run dev </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>In your browser, you will have an output similar to the image below:</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0X_EJPA4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635545169076_got%2Bdata%2Bfrom%2BStrapi.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0X_EJPA4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635545169076_got%2Bdata%2Bfrom%2BStrapi.jpg" alt="" loading="lazy" width="880" height="399"></a></p> <p>After this, we will add functionality to add new questions to Strapi.</p> <h2> <a name="adding-data-to-strapi" href="#adding-data-to-strapi"> </a> Adding data to Strapi </h2> <p>In our <code>Uploadforum.js</code> file, we will add functionality to upload the contents of the form to Strapi. First, we will create two state variables to store the text from our <code>inputs</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { React, useState } from "react"; ... const [name, setName] = useState(""); const [description, setDescription] = useState(""); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Then we set these variables to the value of our form <code>input</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <input type="text" placeholder="Enter your title" maxLength="74" value={name} onChange={(e) => setName(e.target.value)} /> <textarea type="text" placeholder="Enter your description" rows="8" value={description} onChange={(e) => setDescription(e.target.value)} /> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Also we will a function to send these variables when we click our <code>button</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <button onClick={() => sendData()}>Submit Question</button> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We can create the <code>sendData</code> function above our <code>return</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> const sendData = () => { }; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>For our create functionality we will import the <code>createQuestion</code> function we defined in our <code>api</code> folder.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { createQuestion } from "../api"; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Then we pass in our data to this function.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> const sendData = () => { const newQuestion = { Title: name, Questions: description, }; createQuestion(newQuestion); }; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We can now upload new questions to our Strapi <code>collection</code>. We will add the <code>Username</code> when we cover user authentication.</p> <p>Next up, we will add functionality to answer questions in our <code>Displayforum</code> component.</p> <h2> <a name="adding-functionality-to-answer-questions" href="#adding-functionality-to-answer-questions"> </a> Adding Functionality to Answer Questions </h2> <p>Since we set a text field for our answers in our <code>collection</code>, it can only take in one value. To have multiple answers for a question, we will delete the Answers field and create another field for our Answers of type <code>json</code>.</p> <p>With that done, here is an example of what a response from our API would look like:</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6ji2Py_9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635601446784_new%2Bresponse.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6ji2Py_9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635601446784_new%2Bresponse.jpg" alt="" loading="lazy" width="586" height="423"></a></p> <p>To display our answers, we will map through <code>Answers</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> {response.Answers.map((answers, i) => ( <div className={style.eachanswer} key={i}> <p className={style.username}>{response.Answername}</p> <p className={style.answertext}>{answers}</p> </div> ))} </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Now in our browser, if we add more <code>answers</code> to our JSON collection, we can see them displayed on our page.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jt0sMsr0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635602131051_added%2Banswers%2Bjson.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jt0sMsr0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635602131051_added%2Banswers%2Bjson.jpg" alt="" loading="lazy" width="880" height="335"></a></p> <h2> <a name="adding-new-answers" href="#adding-new-answers"> </a> Adding New Answers </h2> <p>We will repeat the same method as we did with our Upload <code>Question</code> functionality for the add answer functionality, but with a minor difference. In your <code>Displayforum</code> component, add the following code:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import axios from "axios"; ... const [answer, setAnswer] = useState("") const [id, setId] = useState(""); const [a, formerArray] = useState([]); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We will store the input from the <code>textarea</code> in <code>answer</code>. We will use the <code>id</code> variable to reference the collection we want to add the answer to.</p> <p>Then in our form <code>textarea</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <textarea type="text" placeholder="Enter your answer" rows="5" value={answer} onChange={(e) => setAnswer(e.target.value)} /> <button onClick={() => { setId(response.id); formerArray(response.Answers); submitAnswer() }}>Post</button> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Then in the <code>submitAnswer</code> function:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> const submitAnswer = () => { try { axios.put(`http://localhost:1337/forums/${id}`, { Answers: [...a, answer], }); } catch (error) { console.log(error); } }; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>With this, we can now add answers through our form to our collection.</p> <h2> <a name="user-authentication-with-nextauth" href="#user-authentication-with-nextauth"> </a> User Authentication with NextAuth </h2> <p>This section will use <a href="https://next-auth.js.org/">Nextauth</a>, a NextJs package for authentication, to implement Google login for our application. We will also set up protected routes so that only authenticated users can create questions and view them.</p> <p>To install <code>next-auth</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code> npm i next-auth </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>For our authentication, we will make use of <a href="https://jwt.io/introduction">JWT token</a> . JWT is a standard used to create access tokens for an application.</p> <p>We will create a file to handle user authentication. To do this, create a folder named <code>auth</code> in your <code>api</code> folder and within it, create a file <code>[...nextauth].js</code> with the following code in it:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import NextAuth from "next-auth"; import GoogleProvider from 'next-auth/providers/google' export default NextAuth({ providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth?prompt=consent&access_type=offline&response_type=code', }) ], jwt: { encryption: true }, secret: process.env.secret, callbacks: { async jwt(token, account) { if (account ?.accessToken) { token.accessToken = account.accessToken } return token; }, redirect: async (url, _baseUrl)=>{ if (url === '/check') { return Promise.resolve('/') } return Promise.resolve('/') } } }); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>The code above sets up our <code>Google Authentication</code> for our app. To use it, we need to wrap up our application in <code>_app.js</code> with the Google Provider component:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> ... import {Provider} from 'next-auth/client' function MyApp({ Component, pageProps }) { return ( <Provider session={pageProps.session}> <Component {...pageProps} /> </Provider> ); } export default MyApp; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Next, we will modify our <code>Displayforum</code> component in to return to our component if the user is authenticated, else it returns a button that leads to an authentication page:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import {signIn, signOut, useSession} from 'next-auth/client' ... const [session, loadingSession] = useSession(); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We will use <code>useSession</code> to know if our user has been authorized.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> if (loadingSession) { <> <p>logging in</p> </>; } return( ... </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>The above code simply returns “logging in” if <code>loadingSession</code> is true. If there is <code>session</code>, we will return the rest of the component and if there is no <code>session</code> we will render a button to <code>sign in</code> to access the app.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> return( <div> {!session && ( <> <h1>Sign in to access forum</h1> <button onClick={() => signIn()}>Sign In</button> </> )} {session && ( <> {/*rest of our app*/} </> )} </div> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We will also set our button to <code>Sign out</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> ... <Link href="/upload"> <button>Ask a question</button> </Link> <button onClick={() => signOut()}>Signout</button> ... </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>To make use of <code>Google authentication</code> in our app, we will require access credentials from Google Cloud console. To get this, navigate in your browser to <a href="https://console.developers.google.com/apis/credentials.">Google Cloud</a>.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8fyVQQWR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635626385264_create%2Bkey.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8fyVQQWR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635626385264_create%2Bkey.jpg" alt="" loading="lazy" width="488" height="309"></a></p> <p>Click on <code>OAuth Client ID</code> and Fill out the fields on the new page that opens.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jdbKsofe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635626440061_create%2Bkey2.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jdbKsofe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635626440061_create%2Bkey2.jpg" alt="" loading="lazy" width="880" height="455"></a></p> <p>Finally, set the redirect URL to: <a href="http://localhost/api/auth/callback/google">http://localhost/api/auth/callback/google</a></p> <p>To use the credentials in the <code>[…nextauth].js</code> file, you can create a <code>.env</code> file and set up your environmental variables:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> GOOGLE_CLIENT_ID: id GOOGLE_CLIENT_SECRET: secret secret: any string </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Next, we will set up our <code>Uploadforum.js</code> component on our <code>upload</code> page as a protected route so that unauthorized users can’t access the route. To do this, in <code>upload.js</code> add the following code:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { getSession, useSession } from 'next-auth/client' </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Then at the bottom:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> export async function getServerSideProps(context) { const session = await getSession(context); if (!session) { context.res.writeHead(302, { Location: '/' }); context.res.end(); return {}; } return { props: { user: session.user, } } } export default Upload; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Now, if you run the app with <code>npm run dev</code> in CLI, we have <code>Google authentication</code> implemented. Also we can’t access the <code>/upload</code> path without logging in.</p> <h2> <a name="adding-user-name-to-collection" href="#adding-user-name-to-collection"> </a> Adding User name to Collection </h2> <p>Now that we have added authentication to our app, we can add the <code>username</code> received from the <code>Google Login</code> as the <code>Answername</code> field when we answer a question:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> ... axios.put(`http://localhost:1337/forums/${id}`, { Answers: [...a, answer], Answername: session.user.name, }); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Now, if I add a new answer to the form:</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lnTi91zI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635629050599_before%2Bpost.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lnTi91zI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635629050599_before%2Bpost.jpg" alt="" loading="lazy" width="880" height="272"></a></p> <p>When I click on the <code>Post</code> button, I get:</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DOQyNCJQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635629083784_After%2Bsubmission.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DOQyNCJQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635629083784_After%2Bsubmission.jpg" alt="" loading="lazy" width="880" height="169"></a></p> <p>The answer has been added and the <code>Answername</code> field has been set to my <code>user.name</code> form our <code>session</code>.</p> <p>Finally, we will also add the <code>username</code> when posting a question to our <code>collection</code>. We will do this in our <code>upload.js</code> file:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> const [session, loadingSession] = useSession(); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Then we pass the value of <code>session</code> to our <code>Uploadforum</code> Component:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <Uploadforum session={session} /> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We can now use <code>session</code> data in our <code>Uploadforum</code> component:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> function Uploadforum({session}) { ... const newQuestion = { Title: name, Questions: description, Answers: [""], Username: session.user.name, }; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Any new questions added now take the <code>Username</code> field to be the <code>username</code> received from <code>session</code>.</p> <p>If we add new answers, since the <code>Answername</code> is a field, it overwrites the previous data and all the <code>answers</code> use the same name. To fix this, we will simply modify our <code>Answers</code> field of type JSON to contain both the answers and the username of the person providing the answers.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XNbqg2mj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635678809482_compounded%2Banswers%2Band%2Busername.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XNbqg2mj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635678809482_compounded%2Banswers%2Band%2Busername.jpg" alt="" loading="lazy" width="880" height="406"></a></p> <p>Then, we can get this data and display it in our <code>Displayforum</code> component:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <div className={style.answers}> {response.Answers.map((answers, i) => ( <div className={style.eachanswer} key={i}> <p className={style.username}>{answers[0]}</p> <p className={style.answertext}>{answers[1]}</p> </div> ))} </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p><code>answer[0]</code> is the name of the user, while <code>answers[1]</code> is the answer.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gkFoOjyL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679166354_compounded%2Banswers%2B.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gkFoOjyL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679166354_compounded%2Banswers%2B.jpg" alt="" loading="lazy" width="880" height="339"></a></p> <p>Finally, we will modify the code to add new answers:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> ... axios.put(`http://localhost:1337/forums/${id}`, { Answers: [...a, [session.user.name, answer]], }); } catch (error) { console.log(error); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We can now add new answers to our questions without overwriting previous data.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g5whN-Z9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679343611_final%2Btest.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5whN-Z9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679343611_final%2Btest.jpg" alt="" loading="lazy" width="880" height="453"></a></p> <p>When I click on post I get a new answer:</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KEWj1FXK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679382631_final%2Bresult.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KEWj1FXK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679382631_final%2Bresult.jpg" alt="" loading="lazy" width="880" height="478"></a></p> <h2> <a name="conclusion" href="#conclusion"> </a> Conclusion </h2> <p>We have come to the end of this tutorial. In this tutorial, we learned how to use Strapi CMS and connect it to NextJ's front-end. In this process, we built a forum site and implemented user authentication and authorization on it.</p> <h2> <a name="resources" href="#resources"> </a> Resources </h2> <p>The source code used in this tutorial can be found in the GitHub repo: <a href="https://github.com/Victory-ET/Strapi-Forumapp">Forum Application</a>.</p>

  • next-auth

    Authentication for the Web.

  • import React, { useState } from "react"; import style from "../../styles/Home.module.css"; import Link from "next/link"; function Displayforum() { const [show, setShow] = useState(false); return ( Display forum Ask a question Login Questions Posted By: Victory Tuduo Description of the Question Answers Post setShow(!show)}> {show ? "Hide Answers" : "Show Answers"} {show ? (

    Miracle

    Try doing it Like this

    ) : null} ); } export default Displayforum;
    Enter fullscreen mode Exit fullscreen mode

    This component handles the layout of our display forum page. We also have a button here that directs the user to the page to upload new questions.

    Meanwhile, in upload.js we have the following:

        import React from "react";
        import Uploadforum from "./Components/Uploadforum";
        function upload() {
          return (
            
    ); } export default upload;
    Enter fullscreen mode Exit fullscreen mode

    Here, we simply added an import for the Uploadforum component into our page. In Uploadforum.js file we have a simple form to create new questions:

        import React from "react";
        import style from "../../styles/Home.module.css";
        import Link from "next/Link";
        function Uploadforum() {
          return (
            

    Ask a question

    Forum
    <button>Submit Question</button> </form> </div> </div> ); } export default Uploadforum; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Finally, we have the following styles in <code>Home.module.css</code><br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> .container { min-height: 100vh; padding: 0 0.5rem; height: 100vh; font-family: monospace; } /* display forum page */ .topcont { display: flex; justify-content: space-between; align-items: center; padding: 5px 8px; } .topcont button, .inputanswer button, .formcont button, .showanswer { border: none; color: #fff; background: dodgerblue; border-radius: 8px; padding: 10px 15px; outline: none; margin: 8px; } .topcont button:hover { cursor: pointer; transform: scale(1.2); } .heading { font-weight: bold; } .subheading { font-weight: 500; text-transform: uppercase; } .userinfo { font-size: 18px; font-weight: 600; } .questioncont { min-height: 300px; padding: 15px 14px; box-shadow: 12px 12px 36px rgba(0, 0, 0, 0.12); } .answercont { min-height: 300px; padding: 5px 3px 5px 15px; } .answers { height: 300px; overflow-x: scroll; } .inputanswer { margin-bottom: 8px; } .inputanswer textarea { width: 100%; resize: none; padding: 5px 8px; } .showanswer { border: 1px solid dodgerblue; background: #fff; color: dodgerblue; transition: 0.4s ease-in-out; } .showanswer:hover { background: dodgerblue; color: #fff; } .eachanswer { border-radius: 15px; background: #e7e7e7; padding: 8px 15px; margin-bottom: 10px; } .username { font-weight: bold; text-transform: uppercase; } .answertext { font-family: Montserrat; font-size: 14px; font-weight: 500; } /* upload a question page */ .uploadpage { min-height: 100vh; } .formcont { min-width: 100vw; display: flex; justify-content: center; align-items: center; } .uploadform { display: flex; flex-direction: column; min-width: 500px; padding-top: 10px; } .uploadform input, .uploadform textarea { resize: none; width: 100%; margin: 8px; padding: 5px; } </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>All of this makes up the layout of our pages.</p> <h2> <a name="getting-data-from-strapi" href="#getting-data-from-strapi"> </a> Getting data from Strapi: </h2> <h2> <a name="setting-up-our-fetch-request" href="#setting-up-our-fetch-request"> </a> Setting up our Fetch Request </h2> <p>In this section, we will fetch our data from Strapi and display it in our app. We will be using <a href="https://axios-http.com/docs/intro">Axios</a> to perform our fetch operations.</p> <p>We will install this via CLI:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code> npm <span class="nb">install </span>axios </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Create a file <code>index.js</code> in the API folder. Here, we will set up our fetch request:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import axios from "axios"; const url = "http://localhost:1337/strapi-forums"; export const readForum = () => axios.get(url); export const createQuestion = (newQuestion) => axios.post(url, newQuestion); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Above, we added import for <code>axios</code>, the URL to fetch our data, and exported functions to read and create data from our forum.</p> <p>We’ll import these functions into our app in our <code>index.js</code> file:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { readForum, createQuestion } from "./api"; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <h2> <a name="fetching-data-from-strapi" href="#fetching-data-from-strapi"> </a> Fetching Data from Strapi </h2> <p>We will fetch the data from Strapi in our <code>index.js</code> file and pass it to <code>Displayforum.js</code> component to display it:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { react, useState, useEffect } from "react"; ... const [question, setQuestions] = useState({}); const [response, setResponse] = useState([]); useEffect(() => { const fetchData = async () => { const result = await readForum(); setResponse(result.data); }; fetchData(); }, []); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Here, we fetched our data from Strapi and assigned it to <code>response</code> with the React <code>useState</code> hook. We have a <code>useEffect</code> function that makes the request when our component mounts.</p> <p>Now, we pass this <code>response</code> down to our <code>Displayforum</code> component.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <Displayforum response={response} /> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <h2> <a name="displaying-data-from-strap" href="#displaying-data-from-strap"> </a> Displaying Data from Strap </h2> <p>To display our data in our <code>Displayforum.js</code> file, we will map our responses and render our components. Before we do this, we will create two additional text fields in our Strapi CollectionCollection: <code>Username</code> and <code>Answername</code>.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--c2XelJbV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635543765246_created%2Bnew%2Bfields.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--c2XelJbV--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635543765246_created%2Bnew%2Bfields.jpg" alt="" loading="lazy" width="792" height="468"></a></p> <p>Back in our <code>Displayforum</code> component we will proceed with displaying our data:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> ... function Displayforum({ response }) { ... {response.map((response, index) => ( <div key={index}> <div className={style.userinfo}> ... <p className={style.answertext}>Try doing it Like this</p> </div> </div> ) : null} </div> </div> ))} </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Here, we wrapped up our components to map through <code>response</code> and display this component as many times as the number of responses. To display our Strapi data, we simply reference it. We can get our <code>Username</code> with this code:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> response.Username </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We can now add this to our component and display it:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <p>Posted By: {response.Username}</p> ... <p className={style.question}>{response.Questions}</p> ... <p className={style.username}>{response.Answername}</p> <p className={style.answertext}>{response.Answers}</p> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We have successfully added the data from our CollectionCollection to our <code>front-end</code> to view this in the browser. Run the following command in the CLI:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code> npm run dev </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>In your browser, you will have an output similar to the image below:</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--0X_EJPA4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635545169076_got%2Bdata%2Bfrom%2BStrapi.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--0X_EJPA4--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635545169076_got%2Bdata%2Bfrom%2BStrapi.jpg" alt="" loading="lazy" width="880" height="399"></a></p> <p>After this, we will add functionality to add new questions to Strapi.</p> <h2> <a name="adding-data-to-strapi" href="#adding-data-to-strapi"> </a> Adding data to Strapi </h2> <p>In our <code>Uploadforum.js</code> file, we will add functionality to upload the contents of the form to Strapi. First, we will create two state variables to store the text from our <code>inputs</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { React, useState } from "react"; ... const [name, setName] = useState(""); const [description, setDescription] = useState(""); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Then we set these variables to the value of our form <code>input</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <input type="text" placeholder="Enter your title" maxLength="74" value={name} onChange={(e) => setName(e.target.value)} /> <textarea type="text" placeholder="Enter your description" rows="8" value={description} onChange={(e) => setDescription(e.target.value)} /> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Also we will a function to send these variables when we click our <code>button</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <button onClick={() => sendData()}>Submit Question</button> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We can create the <code>sendData</code> function above our <code>return</code>.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> const sendData = () => { }; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>For our create functionality we will import the <code>createQuestion</code> function we defined in our <code>api</code> folder.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { createQuestion } from "../api"; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Then we pass in our data to this function.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> const sendData = () => { const newQuestion = { Title: name, Questions: description, }; createQuestion(newQuestion); }; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We can now upload new questions to our Strapi <code>collection</code>. We will add the <code>Username</code> when we cover user authentication.</p> <p>Next up, we will add functionality to answer questions in our <code>Displayforum</code> component.</p> <h2> <a name="adding-functionality-to-answer-questions" href="#adding-functionality-to-answer-questions"> </a> Adding Functionality to Answer Questions </h2> <p>Since we set a text field for our answers in our <code>collection</code>, it can only take in one value. To have multiple answers for a question, we will delete the Answers field and create another field for our Answers of type <code>json</code>.</p> <p>With that done, here is an example of what a response from our API would look like:</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--6ji2Py_9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635601446784_new%2Bresponse.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--6ji2Py_9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635601446784_new%2Bresponse.jpg" alt="" loading="lazy" width="586" height="423"></a></p> <p>To display our answers, we will map through <code>Answers</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> {response.Answers.map((answers, i) => ( <div className={style.eachanswer} key={i}> <p className={style.username}>{response.Answername}</p> <p className={style.answertext}>{answers}</p> </div> ))} </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Now in our browser, if we add more <code>answers</code> to our JSON collection, we can see them displayed on our page.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jt0sMsr0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635602131051_added%2Banswers%2Bjson.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jt0sMsr0--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635602131051_added%2Banswers%2Bjson.jpg" alt="" loading="lazy" width="880" height="335"></a></p> <h2> <a name="adding-new-answers" href="#adding-new-answers"> </a> Adding New Answers </h2> <p>We will repeat the same method as we did with our Upload <code>Question</code> functionality for the add answer functionality, but with a minor difference. In your <code>Displayforum</code> component, add the following code:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import axios from "axios"; ... const [answer, setAnswer] = useState("") const [id, setId] = useState(""); const [a, formerArray] = useState([]); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We will store the input from the <code>textarea</code> in <code>answer</code>. We will use the <code>id</code> variable to reference the collection we want to add the answer to.</p> <p>Then in our form <code>textarea</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <textarea type="text" placeholder="Enter your answer" rows="5" value={answer} onChange={(e) => setAnswer(e.target.value)} /> <button onClick={() => { setId(response.id); formerArray(response.Answers); submitAnswer() }}>Post</button> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Then in the <code>submitAnswer</code> function:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> const submitAnswer = () => { try { axios.put(`http://localhost:1337/forums/${id}`, { Answers: [...a, answer], }); } catch (error) { console.log(error); } }; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>With this, we can now add answers through our form to our collection.</p> <h2> <a name="user-authentication-with-nextauth" href="#user-authentication-with-nextauth"> </a> User Authentication with NextAuth </h2> <p>This section will use <a href="https://next-auth.js.org/">Nextauth</a>, a NextJs package for authentication, to implement Google login for our application. We will also set up protected routes so that only authenticated users can create questions and view them.</p> <p>To install <code>next-auth</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight shell"><code> npm i next-auth </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>For our authentication, we will make use of <a href="https://jwt.io/introduction">JWT token</a> . JWT is a standard used to create access tokens for an application.</p> <p>We will create a file to handle user authentication. To do this, create a folder named <code>auth</code> in your <code>api</code> folder and within it, create a file <code>[...nextauth].js</code> with the following code in it:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import NextAuth from "next-auth"; import GoogleProvider from 'next-auth/providers/google' export default NextAuth({ providers: [ GoogleProvider({ clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, authorizationUrl: 'https://accounts.google.com/o/oauth2/v2/auth?prompt=consent&access_type=offline&response_type=code', }) ], jwt: { encryption: true }, secret: process.env.secret, callbacks: { async jwt(token, account) { if (account ?.accessToken) { token.accessToken = account.accessToken } return token; }, redirect: async (url, _baseUrl)=>{ if (url === '/check') { return Promise.resolve('/') } return Promise.resolve('/') } } }); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>The code above sets up our <code>Google Authentication</code> for our app. To use it, we need to wrap up our application in <code>_app.js</code> with the Google Provider component:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> ... import {Provider} from 'next-auth/client' function MyApp({ Component, pageProps }) { return ( <Provider session={pageProps.session}> <Component {...pageProps} /> </Provider> ); } export default MyApp; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Next, we will modify our <code>Displayforum</code> component in to return to our component if the user is authenticated, else it returns a button that leads to an authentication page:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import {signIn, signOut, useSession} from 'next-auth/client' ... const [session, loadingSession] = useSession(); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We will use <code>useSession</code> to know if our user has been authorized.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> if (loadingSession) { <> <p>logging in</p> </>; } return( ... </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>The above code simply returns “logging in” if <code>loadingSession</code> is true. If there is <code>session</code>, we will return the rest of the component and if there is no <code>session</code> we will render a button to <code>sign in</code> to access the app.<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> return( <div> {!session && ( <> <h1>Sign in to access forum</h1> <button onClick={() => signIn()}>Sign In</button> </> )} {session && ( <> {/*rest of our app*/} </> )} </div> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We will also set our button to <code>Sign out</code>:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> ... <Link href="/upload"> <button>Ask a question</button> </Link> <button onClick={() => signOut()}>Signout</button> ... </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>To make use of <code>Google authentication</code> in our app, we will require access credentials from Google Cloud console. To get this, navigate in your browser to <a href="https://console.developers.google.com/apis/credentials.">Google Cloud</a>.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--8fyVQQWR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635626385264_create%2Bkey.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--8fyVQQWR--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635626385264_create%2Bkey.jpg" alt="" loading="lazy" width="488" height="309"></a></p> <p>Click on <code>OAuth Client ID</code> and Fill out the fields on the new page that opens.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jdbKsofe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635626440061_create%2Bkey2.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jdbKsofe--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635626440061_create%2Bkey2.jpg" alt="" loading="lazy" width="880" height="455"></a></p> <p>Finally, set the redirect URL to: <a href="http://localhost/api/auth/callback/google">http://localhost/api/auth/callback/google</a></p> <p>To use the credentials in the <code>[…nextauth].js</code> file, you can create a <code>.env</code> file and set up your environmental variables:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> GOOGLE_CLIENT_ID: id GOOGLE_CLIENT_SECRET: secret secret: any string </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Next, we will set up our <code>Uploadforum.js</code> component on our <code>upload</code> page as a protected route so that unauthorized users can’t access the route. To do this, in <code>upload.js</code> add the following code:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> import { getSession, useSession } from 'next-auth/client' </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Then at the bottom:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> export async function getServerSideProps(context) { const session = await getSession(context); if (!session) { context.res.writeHead(302, { Location: '/' }); context.res.end(); return {}; } return { props: { user: session.user, } } } export default Upload; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Now, if you run the app with <code>npm run dev</code> in CLI, we have <code>Google authentication</code> implemented. Also we can’t access the <code>/upload</code> path without logging in.</p> <h2> <a name="adding-user-name-to-collection" href="#adding-user-name-to-collection"> </a> Adding User name to Collection </h2> <p>Now that we have added authentication to our app, we can add the <code>username</code> received from the <code>Google Login</code> as the <code>Answername</code> field when we answer a question:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> ... axios.put(`http://localhost:1337/forums/${id}`, { Answers: [...a, answer], Answername: session.user.name, }); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Now, if I add a new answer to the form:</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--lnTi91zI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635629050599_before%2Bpost.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--lnTi91zI--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635629050599_before%2Bpost.jpg" alt="" loading="lazy" width="880" height="272"></a></p> <p>When I click on the <code>Post</code> button, I get:</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--DOQyNCJQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635629083784_After%2Bsubmission.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--DOQyNCJQ--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635629083784_After%2Bsubmission.jpg" alt="" loading="lazy" width="880" height="169"></a></p> <p>The answer has been added and the <code>Answername</code> field has been set to my <code>user.name</code> form our <code>session</code>.</p> <p>Finally, we will also add the <code>username</code> when posting a question to our <code>collection</code>. We will do this in our <code>upload.js</code> file:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> const [session, loadingSession] = useSession(); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Then we pass the value of <code>session</code> to our <code>Uploadforum</code> Component:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <Uploadforum session={session} /> </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We can now use <code>session</code> data in our <code>Uploadforum</code> component:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> function Uploadforum({session}) { ... const newQuestion = { Title: name, Questions: description, Answers: [""], Username: session.user.name, }; </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>Any new questions added now take the <code>Username</code> field to be the <code>username</code> received from <code>session</code>.</p> <p>If we add new answers, since the <code>Answername</code> is a field, it overwrites the previous data and all the <code>answers</code> use the same name. To fix this, we will simply modify our <code>Answers</code> field of type JSON to contain both the answers and the username of the person providing the answers.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--XNbqg2mj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635678809482_compounded%2Banswers%2Band%2Busername.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--XNbqg2mj--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635678809482_compounded%2Banswers%2Band%2Busername.jpg" alt="" loading="lazy" width="880" height="406"></a></p> <p>Then, we can get this data and display it in our <code>Displayforum</code> component:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> <div className={style.answers}> {response.Answers.map((answers, i) => ( <div className={style.eachanswer} key={i}> <p className={style.username}>{answers[0]}</p> <p className={style.answertext}>{answers[1]}</p> </div> ))} </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p><code>answer[0]</code> is the name of the user, while <code>answers[1]</code> is the answer.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--gkFoOjyL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679166354_compounded%2Banswers%2B.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--gkFoOjyL--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679166354_compounded%2Banswers%2B.jpg" alt="" loading="lazy" width="880" height="339"></a></p> <p>Finally, we will modify the code to add new answers:<br> </p> <div class="highlight js-code-highlight"> <pre class="highlight plaintext"><code> ... axios.put(`http://localhost:1337/forums/${id}`, { Answers: [...a, [session.user.name, answer]], }); } catch (error) { console.log(error); </code></pre> <div class="highlight__panel js-actions-panel"> <div class="highlight__panel-action js-fullscreen-code-action"> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-on"><title>Enter fullscreen mode</title> <path d="M16 3h6v6h-2V5h-4V3zM2 3h6v2H4v4H2V3zm18 16v-4h2v6h-6v-2h4zM4 19h4v2H2v-6h2v4z"></path> </svg> <svg xmlns="http://www.w3.org/2000/svg" width="20px" height="20px" viewbox="0 0 24 24" class="highlight-action crayons-icon highlight-action--fullscreen-off"><title>Exit fullscreen mode</title> <path d="M18 7h4v2h-6V3h2v4zM8 9H2V7h4V3h2v6zm10 8v4h-2v-6h6v2h-4zM8 15v6H6v-4H2v-2h6z"></path> </svg> </div> </div> </div> <p>We can now add new answers to our questions without overwriting previous data.</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--g5whN-Z9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679343611_final%2Btest.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--g5whN-Z9--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679343611_final%2Btest.jpg" alt="" loading="lazy" width="880" height="453"></a></p> <p>When I click on post I get a new answer:</p> <p><a href="https://res.cloudinary.com/practicaldev/image/fetch/s--KEWj1FXK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679382631_final%2Bresult.jpg" class="article-body-image-wrapper"><img src="https://res.cloudinary.com/practicaldev/image/fetch/s--KEWj1FXK--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://paper-attachments.dropbox.com/s_C85777D6DFCF98E4EF331F9FA47DA97E3213CAFEB9DAD61A99F14E2CE8154E5F_1635679382631_final%2Bresult.jpg" alt="" loading="lazy" width="880" height="478"></a></p> <h2> <a name="conclusion" href="#conclusion"> </a> Conclusion </h2> <p>We have come to the end of this tutorial. In this tutorial, we learned how to use Strapi CMS and connect it to NextJ's front-end. In this process, we built a forum site and implemented user authentication and authorization on it.</p> <h2> <a name="resources" href="#resources"> </a> Resources </h2> <p>The source code used in this tutorial can be found in the GitHub repo: <a href="https://github.com/Victory-ET/Strapi-Forumapp">Forum Application</a>.</p>

NOTE: The number of mentions on this list indicates mentions on common posts plus user suggested alternatives. Hence, a higher number means a more popular project.

Suggest a related project

Related posts