community-content
Strapi-Forumapp
community-content | Strapi-Forumapp | |
---|---|---|
12 | 2 | |
563 | 18 | |
1.4% | - | |
8.1 | 0.0 | |
28 days ago | almost 2 years ago | |
JavaScript | JavaScript | |
- | - |
Stars - the number of stars that a project has on GitHub. Growth - month over month growth in stars.
Activity is a relative number indicating how actively a project is being developed. Recent commits have higher weight than older ones.
For example, an activity of 9.0 indicates that a project is amongst the top 10% of the most actively developed projects that we are tracking.
community-content
-
Goodbye 2022, Hello 2023! Strapi Wrapped in One Year
Strapi wouldn’t be anything without its community, which is very much represented by its Community Stars. The Write for the Community program resulted in 148 new articles being published, for a total of 1.3M views. 2022 was also the year of the launch of the Strapi Community Organization, a group of community members dedicated to empowering initiatives and highlighting them. Boaz, Mattie, Sacha, and Simen have been invaluable contributors to the Strapi Community, going above and beyond by developing open-source plugins and tools. Strapi config-sync plugin, mattie-strapi-bundle (for search), Strapi REST cache plugin, Dockerize tool, and more!
-
Celebrating 50K GitHub Stars
None of this would have been possible without the amazing Strapi community. To reinforce our commitment to open-source, we’d like to highlight the amazing work made by the Strapi Community Organization, thank the organizers of Strapi events, and acknowledge the amazing content made by the creators of the Write for the Community program.
-
Best Practices for Onboarding Content Managers to Your Strapi CMS
💡 Tips: Instead of creating all your components from scratch, you can pick up examples directly from the strapi.io website and the official FoodAdvisor Demo.
-
A Guide to Technical Writing
With practice comes perfection. The only way to be a great technical writer is to write more. The more you write, the more you learn and get better. Once you are comfortable writing for the Write For the Community program at Strapi, you may also venture into contributing to making our documentation better.
-
How to Build a Forum App with NextJs and Strapi CMS
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
-
Get Paid to Write for These 45+ Websites
Strapi
-
Create a Preview Button in Strapi V3 for Next.js
I'm taking this opportunity to thank them because this use case is very requested and their articles answer to a lot of questions. I want to emphasize that this type of contribution is very important for the Strapi community and if you wish to be part of it, I invite you to join our Write for the Community Program.
-
How the Strapi Marketing Team Uses Strapi
Open-Source: Anyone can contribute to the source code and thus help, improve, fix issues, propose ideas, etc... Thanks to the community efforts, Strapi will always stay updated and product requests have more chance to be met. The project evolves next to the community and it is priceless. Beyond the code contributions, our community create a lot of educational resources like articles thanks to our Write for the Community program, videos on youtube, webinars, or plugins & providers that you'll probably use at some point: Awesome Strapi repository
-
How to Implement Previews with Next Applications using a Strapi backend
. ## Conclusion The preview mode is an essential Static site generator tool that can improve the content editor experience when using the Jamstack architecture. To learn more about Next.js preview mode, visit this link - [](https://nextjs.org/docs/advanced-features/preview-mode)https://nextjs.org/docs/advanced-features/preview-mode [](https://nextjs.org/docs/advanced-features/preview-mode). Source Code - https://github.com/Quadrisheriff/previews-tutorial- >This article is a guest post by [**Quadri Sheriff**](http://twitter.com/QuadriSheriff3). He wrote this blog post through the [Write for the Community](https://strapi.io/write-for-the-community) program.
-
Strapi Draft System Explained
This article is a guest post by Precious Luke. He wrote this blog post through the Write for the Community program.
Strapi-Forumapp
-
How to Build a Forum App with Next.Js and Strapi CMS
The source code used in this tutorial can be found in the GitHub repo: Forum Application.
-
How to Build a Forum App with NextJs and Strapi CMS
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
Enter fullscreen mode Exit fullscreen modeThis 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 (
Enter fullscreen mode Exit fullscreen modeHere, we simply added an import for the
Uploadforum
component into our page. InUploadforum.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>
What are some alternatives?
reactour - Tourist Guide into your React Components
Next.js - The React Framework
strapi-template-blog - Template to create Strapi projects pre-configured for blogs
next-auth - Authentication for the Web.
strapi-starter-gatsby-blog - Updated version of the first Gatsby starter with much more features
Strapi - 🚀 Strapi is the leading open-source headless CMS. It’s 100% JavaScript/TypeScript, fully customizable and developer-first.
jamstack.org - The official Jamstack site
awesome-strapi - A curated list of awesome things related to Strapi
engineering-education - “Section's Engineering Education (EngEd) Program is dedicated to offering a unique quality community experience for computer science university students."
foodadvisor - 🥘 THE Strapi demo application
Tailwind CSS - A utility-first CSS framework for rapid UI development.
strapi-plugin-config-sync - :recycle: CLI & GUI for continuous migration of config data across environments