Understanding and using Relations in Strapi
Tags: strapi, headless, cms
Published: 2021-09-28 13:32:13

In this article, we will look into the relational fields in Strapi to see how we can utilize them to establish relationships in our models

The tech on focus here is Strapi.

## **What is Strapi?**

[Strapi](https://strapi.io/) is an open-source Node.js headless CMS (Content Management System) based on Nodejs used to develop APIs(RESTful and GraphQL APIs) and build the APIs content. The APIs in Strapi is built in the form of collections or single types.

A collection in Strapi will create and expose the endpoints on all the HTTP verbs. For example, if we have a blog collection. Strapi will create the following endpoints based on the collection:


- `blog` **GET**: This will get all the blog entries in the endpoint.
- `blog` **POST**: This will create a new blog post in the endpoint.
- `blog/:id` **GET**: This will return the blog post with the id `:id`.
- `blog/:id` **DELETE**: This will delete a blog post with the id `:id` from the endpoint.

So, Strapi creates all those APIs for us. We can then add content to the collection via the admin panel or the Strapi API. 

Internally, Strapi is powered by [Koajs](https://koajs.com/), and its default database is [SQLite](https://www.sqlite.org/), where it persists the content we add to the collections and single-types. We have learned a bit about Strapi. In the next sections, we will learn about relations in database models and establish the relations in Strapi collections.

## **Relations in Database fields and Strapi**

A database contains tables, columns, and records. Now, relationships can be defined in the database tables. In Strapi, we can use relations to create links between our Content Types. This relationship is like a pointer or reference. They point to data in a table that depicts what they contain.

There are types of relationships we can establish in Strapi:

- One-to-one (1:1)
- One-to-Many
- Many-to-Many
- One-Way
- Many-way
- Polymorphic

**One-to-One (1:1)** In this one-to-one relationship, a column in a table points to **only one** column in another table.

**One-to-One (1:1)** In this one-to-one relationship, a column in a table points to **only one** column in another table.

For example, in a `Student` table, a `studentId` column can point to a `StudentInfo` table. 
A column in the `StudentInfo` table, `studentId` points back to the `Student` table. 
So here, the `Student` table is associated with one and only one record in the `StudentInfo` table.

So we can fetch a student info from the `Student` table, and we can fetch a student from the `StudentInfo` table. That's a **one-to-one** relationship.

**One-to-Many** This relation involves a table pointing to several or many tables. A column in table A can point to several tables(B, C, D, E), these tables in turn point to table A. Also, each table (A, B, C, D) can hold one or more records of the column in table A.

For example, let's say we have a `Company` table. This table holds the list of all the companies in a system. Now, we can create an `Employee` table to hold the name of an employee. Now, we can add a `companyId` column to the Employee table, and this `companyId` will point to the `Company` table.

So, now a `Company` table can point to many employed records in the `Employee` table. Also, each record in the `Employee` table points back to a record in the `Company` table. The relation here is **one-to-many**.

**The many-to-Many** relationship involves a column in a table pointing to many records in another table and a column in another table pointing to many records in the first table.

For example, many **doctors** can be associated with many **hospitals**. Now, we have learned the types of relationships, let's see how we can implement them in a Strapi collection.

**One-Way** This relationship involves a column pointing or linking to another column in a table. The thing here is that the other column does not point back to the "pointing" column. One-way relation is similar to One-to-One relation but differs because the column being "pointed" does not link back to the pointing column.

For example, in a `User` table, A `detailsId` column in the `User` table can point to a `Details` table. This means that the details of a user are in the `detailsId` column in the `User` table and the details are stored in the `Details` table.

So we see that the `User` table points to only one table, which is the `Details` table. The relationship is **one-way**. There is no column in the `Details` table that points back to the `User` table.

**Many-way** This relation involves a column in a table pointing to many records in another table. The records being pointed to does not point back or link back to the record.

For example, a `User` table has a column `carId` that points to a `Car` table. The `carId` can point to many records in the `Car` table but the `Car` record does not point back to the `User` table, this relationship is a `many-way` relationship.

**Polymorphic** This relationship involves a column in a table that can link to different columns in other tables. In a polymorphic relationship, a model/table can be associated with different models/tables. 

In other relationships we have seen, it is mainly between a table and another table, not more than three tables are involved in the relationship. But in a polymorphic relationship, multiple tables are involved.

For example, a `Tire` table holds can be linked and have links to a `Toyota` table, `Mercedes` table, etc. So a `Toyota` can relate to the same `Tire` as a `Mercedes`.

We have seen all the relations we have. The below sections will explain and show how we can set the relations from both the Strapi admin UI and a Strapi project.

**Where are relationships set in Strapi?**
Relationship links can be set in the Admin panel and manually from the generated Strapi project.

***via Strapi Admin panel***
Relations can be set in Strapi's Collection types, Single types, and Components. The relation is set when adding fields to our Collection, Single collection, or Component type. The `relation` field is selected:


![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631616086416_Screenshot+2021-09-13+at+12.44.18.png)


Another UI is displayed in the modal:


![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631616101550_Screenshot+2021-09-13+at+12.45.17.png)


This is where we set the relations between the current model we are creating and an existing model. 

We have two big boxes in the above picture, the left box is the current model we are creating, and the right box is the model the current model will be having relations with. We can click on the dropdown icon to select the model we want to link relations within the right box.


![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631616473240_Screenshot+2021-09-13+at+12.45.23.png)


The smaller boxes with icons are the relations we can establish between the two models in the bigger boxes.

Let's look at the smaller boxes starting from the left.


![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631616667207_Untitled.png)


The **first box** represents the `has one` relation. 

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631616810974_Screenshot+2021-09-14+at+11.52.42.png)


It establishes a `one-way` relation between content types in Strapi.


The **second box** is `has one and belongs to one`. 

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631616836811_Screenshot+2021-09-14+at+11.52.46.png)


It links two content types in a `one-to-one way` relationship.


The **third box** is `belongs to many`. 

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631616858884_Screenshot+2021-09-14+at+11.52.49.png)


It links two content types in a `one-to-many` relation. The content type in the left-bigger box will have a field that links to many records in the content type that is in the right-bigger box. The field in the content type in the right-bigger box will have a field that links to a single record in the left-content type.


The **fourth box** is `has many`. 

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631616879134_Screenshot+2021-09-14+at+11.52.52.png)


This one links two content types in a `many-to-one` relation. Here, the content type at the left-bigger box has a field that links to many records to the content type at the right-bigger box. It is the reverse of the `belongs to many` boxes.


The **fifth box** is `has and belongs to many`. 

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631616906539_Screenshot+2021-09-14+at+11.52.55.png)


This box links two content types in a `many-to-many` relationship. Both content types in the bigger boxes will have a field that links many records to each other.


The **sixth box** is `has many`. 

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631616921800_Screenshot+2021-09-14+at+11.52.59.png)


It links two content types in a `many-way` relationship. The field on the left content type links to many records in the right content type. The right content type does not link back to the left content type.


## *via Strapi project*

Let's see how we set relations in our content types from our Strapi project. The content types in a Strapi are stored in the `api` folder in our Strapi project. The relations are set in the `./api/[NAME]/models/[NAME].settings.json` file.

Fields are set inside the `attributes` section. To set a relation field we use some properties like `model`, `collection`, etc. Let's see how we set the relations for all the types of relations in Strapi.

**One-to-one (1:1)**
To set a one-to-one relation between two content types we will use the `model` and `via` properties. Let's say we want to set a one-to-one between a `Student` model and a `StudentInfo` model, we will open the `./api/student/models/student.settings.json` file and add the code:

```
    {
      "attributes": {
        "studentinfo": {
          "model": "studentinfo",
          "via": "student"
        }
      }
    }
```

The relation field is `studentinfo`. The `model` refers to the content type in Strapi the field is pointing to. It is set to `studentinfo` so the `studentinfo` property in the `Student` content type points to the `StudentInfo` content type.
The `via` property is set to `student`. All these state that the `Student` model has and belongs to one `StudentInfo`.

Let's see inside `./api/studentinfo/models/studentinfo.settings.json` file

```
      "attributes": {
        "student": {
          "model": "student",
          "via": "studentinfo"
        }
      }
```

Here, we have a `student` relation field. The properties `model` and `via` point to `student` and `studentinfo` respectively.
These two JSON configs of both `Student` and `StudentInfo` models establish a one-to-one relationship between them.

**One-to-Many**
To set a one-to-many manually, we will use the `collection`, `model`, and `via` properties.
Let's say we have two content types, `Employee` and `Company`. The `Company` has many `Employee` records, and the `Employee` record points back to a `Company` record.

To establish this in the content types, we will go to their `model/.settings.json` files in our project and set relation fields.

For the `Company` model, we want an `employees` relation to point to many `Employees`. So we will do the below in the `/company/model/company.settings.json` file.

```
      "attributes": {
        "employees": {
          "collection": "employee",
          "via": "company"
        }
      }
```

Also, in `/employee/model/employee.settings.json` file:

```
      "attributes": {
        "company": {
          "via": "employees",
          "model": "company"
        }
      }
```

This sets a one-to-many relationship in the `Company` model.

**Many-to-Many**
In setting a many-to-many relation from our Strapi project, we will set the `collection` and `via` properties in the relation fields of both content types.
For example, doctors can work in many hospitals and many hospitals can have many doctors. In this case, our `Doctor` model will be this:

```
      "attributes": {
        "hospitals": {
          "via": "doctors",
          "collection": "hospital"
        }
      }
```

The `hospital` relation field points to many hospitals. The `collection` property set to `hospital` tells Strapi that the `hospital` relation field points to many records in the Hospital model. The `via` property set to `doctors` tells Strapi to select the hospitals from the `doctors` relation field in the `Hospital` model.

The `Hospital` model will be this:
`/hospital/models/hospital.settings.json`:

```
      "attributes": {
        "doctors": {
          "via": "hospitals",
          "collection": "doctor"
        }
      }
```

This effectively sets a many-to-many relation between the Doctor and Hospital models.

**One-Way**
To set this relation from our Strapi project between two models, we will define a relation field in one model's `.settings.json` file only. The other model will have no relation connecting to other model define in its `.settings.json` file.

For example, we have two models `User` and `Detail` and they have one-way relation. To set this up. We set the below in the `User`'s model file `user/models/user.settings.json` file:

```
      "attributes": {
        "detail": {
          "model": "detail"
        }
      }
```

The `detail` object right under the `attributes` key is the relation field. The `model` set to `detail` tells Strapi that this field points to one `Detail` model record.
There will be no relation setting in the `Detail` model file that will point to the `User` model. SO in this way, we have set a one-way relation between the `User` and `Detail` models in Strapi.

**Many-way**
This is the same as the one-way relation but this one involves one model pointing to many records in another model, but this another model does not point back.
To set this manually in Strapi, we will set a relation field with the `collection` property in one model but no relation definition in the other model.

For example, a `User` has many `Car`s. The relation is many-way. A user can own many cars. The setting will be this for the `User`:
`user/models/user.settings.json`:

```
      "attributes": {
        "cars": {
          "collection": "car"
        }
      }
```

The `car` relation has a `collection` property that is set to `car`. This setting tells Strapi that the `cars` field in the `User` model points to many `Car` records.
We will not make a relation in the `Car` model that will point back to the `User` model because this is a many-way relation.

**Polymorphic**
We use the `collection`, `model`, and `filter` keys to set up a polymorphic relationship in Strapi from our `.settings.json` file.

The `collection` key will have the value `*` if we want the model to have many model entries.
The `model` key also will be set to have the `*` value if we want the model to have one model entry.
The `filter` key is useful because it distinguishes between different types of models attached to the field.

We have learned all the relations in Strapi and also learned how to set them up both via the Strapi admin UI panel and from a Strapi project. Now, we show how to use some of the relations in Strapi to build a real-life app.

## **Setting up Strapi project**

We will create a Q/A app just like Quora, and users can ask questions, answer questions, and comment on answers. We will build this app so to demonstrate how we can use Strapi relations to link our models.

This project will be in two parts: the backend and the frontend. Of course, the backend will be built using Strapi, and the frontend will be built using Next.js.

We will create a central folder that will hold our both backend and frontend projects:

```
    mkdir relations
```

We move into the folder: `cd relations`.

Create the Strapi project:

```bash
    yarn create strapi-app qa-app --quickstart
```

The above command will create a Strapi project in `qa-app` folder inside the `relations` folder. Strapi will serve the project on `localhost:1337`. It will launch the Strapi admin UI panel on `localhost:1337/admin`.
Fill in your details and click on "LET'S START" button.
We will begin to build our collections but first, let's draw our models.

## Models

We will have three models for our Q/A app. We will have `Question`, `Answer` and `Comment`.

Our `Question` model will be this:

```
    Question {
        qText
        user
    }
```

- `qText`: This will hold the question.
- `user`: This holds the name of the user.

The `Answer` model will be this:

```
    Answer {
        aText
        question
        user
    }
```

- `aText`: This holds the answer text.
- `question`: This holds the reference to the question.
- `user`: The user that answered.

The `Comment` model will look like this:

```
    Comment {
        cText
        answer
        user
    }
```

- `cText`: This will hold the comment text on the answer.
- `answer`: This is the reference to the answer.
- `user`: The user that commented.

We have seen how our collection will look like, now let's build our collections. These models have relationships that connect them. Let's see them below.

## One-to-Many

The `Question` model and the `Answer` model have a one-to-many relationship. A Question will have many Answers.

Now, we will build a `Question` collection in Strapi, and also we will create the `Answer` collection and there we will establish the relation between them.
Now, on the [http://localhost:1337/admin/](http://localhost:1337/admin/) page click on the `Create First Content Type` button, a modal will appear.

We will create the `**Question**` collection.

- Type `question` in the `Display name` field.
- Click on the text field.
- Type `qText` in the `Name` field.
- Select `Long Text` in the below radio button.
- Click on `+ Add another field`.
- Select `text`.
- Type in `user`.
- Click on `Finish`.
- Next, click on the `Save` button on the top-right of the page.

Next, we will create the `**Answer**` collection

- Click on the `+ Create new collection type` link, a modal will show up, type in `answer`. Click on the `+ Add another field` button.
- Select `text` and type in `user`.
- Select `relation` field.
- On the right box, press on the dropdown element and select `Question`.
- Click on the fourth small box, counting from left. The box establishes a **one-to-many** relationship between the `Question` collection and the `Answer` collection.
- Click on the `Finish` button.
- Next, click on the `Save` button on the top-right of the page.

## One-to-one

The `Comment` model and the `Answer` model have a one-to-one relationship. A comment has one answer.
We will create the Comment collection.

- Click on the `+ Create new collection type` link, a modal will show up, type in `comment`. Click on the `+ Add another field` button.
- Select `text` field.
- type in `cText` and click on the `+ Add another field` button.
- Select `relation` field.
- On the big box on the right, click on the dropdown element and select `Answer`.
- Select the first small box, counting from the left. This box establishes the **one-to-one** relationship between the `Comment` and the `Answer`.
- Click on the `Finish` button.
- Next, click on the `Save` button on the top-right of the page.

We are done building our collections and establishing their relationships. Now, let's build the front end.

## **Building the Q/A app**

Our app will have two pages: the index and the question view page.

- `/` index: This page will display all questions in the app.
- `/questions/:id`: This page is a dynamic page. It will display the details of a specific question. The details displayed are the answers to the question and the comments are replies to the answers.

Our app will look like this:

**Viewing all questions**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631210187835_Screenshot+2021-09-09+at+14.58.19.png)


**Adding new question**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631210353545_Screenshot+2021-09-09+at+14.56.51.png)


**Answering a question**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631210220785_Screenshot+2021-09-09+at+14.58.57.png)


**Commenting on an answer**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631210247493_Screenshot+2021-09-09+at+14.59.14.png)


**Deleting a question**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631210201280_Screenshot+2021-09-09+at+14.57.16.png)


**Viewing all answers and comments**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631210261967_Screenshot+2021-09-09+at+14.57.01.png)

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631210281297_Screenshot+2021-09-09+at+14.57.08.png)


We will start by scaffolding a Nextjs app. We will scaffold the Nextjs app inside the `relations` folder, so run the below command:

```bash
    yarn create next-app qa-front
```

Now, we move into the directory: `cd qa-front`.

We will need the following dependencies:

- `axios`: We will need this for making HTTP calls to our Strapi collection endpoints.
- `quill`: An editor we will use for answering questions in our app.

We will install axios via NPM:

```bash
    yarn add axios
```

We install and use quill via CDN. Open the `_app.js` file and add the following code to it:

```
    import "../styles/globals.css";
    import Head from "next/head";
    
    function MyApp({ Component, pageProps }) {
      return (
        <>
          <Head>
            <script src="https://cdn.quilljs.com/1.3.6/quill.min.js"></script>
    
            <link
              href="https://cdn.quilljs.com/1.3.6/quill.snow.css"
              rel="stylesheet"
            />
            <link
              href="https://cdn.quilljs.com/1.3.6/quill.bubble.css"
              rel="stylesheet"
            />
          </Head>
          <Component {...pageProps} />
        </>
      );
    }
    
    export default MyApp;
```

We added the CDN scripts and style files of the `quill` editor to the `Head` tags of the `MyApp` component.

## Creating Pages

First, we will create a `Header` component, this component will render our header so it appears in our app.

Run the below command to generate the `Header` files:

```
    mkdir components components/Header
    touch components/Header/index.js components/Header/Header.module.css
```

Now, we open the `Header/index.js` and paste the below code to it:

```
    import { header, headerName } from "./Header.module.css";
    
    export default function Header() {
      return (
        <section className={header}>
          <div className={headerName}>Q/A App</div>
        </section>
      );
    }
```

This component just renders the text `Q/A App` in the header section of our app.
Now, to make the component appear application-wide in our app we will go the the `MyApp` component in `_app.js` file and render the component.

```
    import "../styles/globals.css";
    import Header from "../components/Header";
    import Head from "next/head";
    
    function MyApp({ Component, pageProps }) {
      return (
        <>
          <Head>...</Head>
          <Header />
          <Component {...pageProps} />
        </>
      );
    }
    
    export default MyApp;
```

With this, our `Header` component will be rendered on all pages in our application.
Let's create our page components.
The `index.js` page will be loaded when the index route `/` is navigated to.
So, open the index.js file and paste the below code to it:

```
    import Head from "next/head";
    import styles from "../styles/Home.module.css";
    import QuestionCard from "../components/QuestionCard";
    import { useEffect, useState } from "react";
    import axios from "axios";
    import AddQuestionDialog from "../components/AddQuestionDialog";
    
    export default function Home() {
      const [questions, setQuestions] = useState([]);
      const [showAddQuestionModal, setShowAddQuestionModal] = useState(false);
    
      useEffect(async () => {
        const data = await axios.get("http://localhost:1337/questions");
        setQuestions(data?.data);
      }, []);
    
      return (
        <div className={styles.container}>
          <Head>
            <title>Q/A App</title>
            <link rel="icon" href="/favicon.ico" />
          </Head>
    
          <main className={styles.main}>
            <div className={styles.breadcrumb}>
              <div>
                <span style={{ margin: "1px" }}>
                  <button
                    style={{
                      backgroundColor: "rgba(185, 43, 39, 1)",
                      border: "1px solid rgba(101, 20, 18, 1)",
                    }}
                    onClick={() => setShowAddQuestionModal(true)}
                  >
                    Add Question
                  </button>
                </span>
              </div>
            </div>
    
            <div className={styles.questioncontainerr}>
              <div>
                {questions
                  ?.sort((a, b) => b.created_at.localeCompare(a.created_at))
                  .map((question, i) => (
                    <QuestionCard key={i} question={question} />
                  ))}
              </div>
            </div>
            {showAddQuestionModal ? (
              <AddQuestionDialog
                closeModal={() => setShowAddQuestionModal((pV) => !pV)}
              />
            ) : null}
          </main>
        </div>
      );
    }
```

First, we have two states: `questions` and `showAddQuestionModal`. The `questions` state holds the questions loaded from our Strapi backend. The `showAddQuestionModal` state is used to toggle the display of the `AddQuestionsModal` on and off.

We have the `useEffect`. In the callback of this `useEffect`, we used the `axios` module to call the Questions endpoint [http://localhost:1337/questions](http://localhost:1337/questions). This endpoint will return all the questions in our backend. 

Next, we set the result in the `questions` state.

In the UI, we have an `Add Question` button that will set the `showAddQuestionModal` state to true when clicked. This will make the `AddQuestionDialog` modal show up.

The questions in the `questions` state are rendered. Each question in the `questions` array is rendered by a `QuestionCard` component. Now, we will create two components: `QuestionCard` and `AddQuestionDialog`.

**QuestionCard**
This presentational component will accept a question object and render a minimal detail of it. Let's create the files and folder.

```
    mkdir components/QuestionCard
    touch components/QuestionCard/index.js
    touch components/QuestionCard/QuestionCard.module.css
```

Open the `index.js` and paste the below code:

```
    import styles from "./QuestionCard.module.css";
    import Link from "next/link";
    
    export default function QuestionCard({ question }) {
      const { id, qText, user, answers } = question;
    
      return (
        <div className={styles.question}>
          <div className={styles.questiondetails}>
            <div
              style={{
                display: "flex",
                alignItems: "center",
              }}
            >
              <span
                style={{
                  display: "block",
                  width: "35px",
                  height: "35px",
                  backgroundColor: "grey",
                  borderRadius: "50%",
                }}
              ></span>
              <span style={{ paddingLeft: "4px" }}>{user}</span>
            </div>
            <Link href={`questions/${id}`}>
              <div className={styles.questionbalance} style={{ cursor: "pointer" }}>
                <h3>{qText}</h3>
              </div>
            </Link>
            <div
              style={{ display: "flex", alignItems: "center", color: "grey" }}
              title="Answers"
            >
              <MsgIcon />
              <span style={{ paddingLeft: "6px" }}>{answers.length}</span>
            </div>
          </div>
        </div>
      );
    }
    
    export function MsgIcon() {
      return (
        <svg width="24px" height="24px" viewBox="0 0 24 24">
          <g
            id="comment"
            className="icon_svg-stroke icon_svg-fill"
            stroke="#666"
            strokeWidth="1.5"
            fill="none"
            fillRule="evenodd"
          >
            <path d="M12.0711496,18.8605911 C16.1739904,18.8605911 19.5,15.7577921 19.5,11.9302955 C19.5,8.102799 16.1739904,5 12.0711496,5 C7.96830883,5 4.64229922,8.102799 4.64229922,11.9302955 C4.64229922,13.221057 5.02055525,14.429401 5.67929998,15.4641215 C5.99817082,15.9649865 4.1279592,18.5219189 4.56718515,18.9310749 C5.02745574,19.3598348 7.80252458,17.6358115 8.37002246,17.9406001 C9.45969688,18.5258363 10.7235179,18.8605911 12.0711496,18.8605911 Z"></path>
          </g>
        </svg>
      );
    }
```


We destructured `question` from the prop args. Next, we destructured the id, `qText`, user, and `answers` from the `question` object. This `question` object contains the question details. The component then renders these details.


## AddQuestionDialog

This component is a modal. New questions are created and added to this system from this component.

Let's create the files:

```
    mkdir components/AddQuestionDialog
    touch components/AddQuestionDialog/index.js
```


Open the `index.js` and paste the below code:

```
    import { useState } from "react";
    import axios from "axios";
    
    export default function AddQuestionDialog({ closeModal }) {
      const [disable, setDisable] = useState(false);
    
      async function addQuestion() {
        setDisable(true);
        const qText = window.question.value;
        const user = window.user.value;
    
        // add question
        await axios.post("http://localhost:1337/questions", {
          qText,
          user,
        });
        setDisable(false);
        closeModal();
        location.reload();
      }
    
      return (
        <div className="modal">
          <div className="modal-backdrop" onClick={closeModal}></div>
          <div className="modal-content">
            <div className="modal-header">
              <h3>Add New Question</h3>
              <span
                style={{ padding: "10px", cursor: "pointer" }}
                onClick={closeModal}
              >
                X
              </span>
            </div>
            <div className="modal-body content">
              <div style={{ display: "flex", flexDirection: "column" }}>
                <div className="inputField">
                  <div className="label">
                    <label>User</label>
                  </div>
                  <div>
                    <input id="user" type="text" />
                  </div>
                </div>
                <div className="inputField">
                  <div className="label">
                    <label>Question:</label>
                  </div>
                  <div>
                    <input
                      id="question"
                      type="text"
                      placeholder="Start your question with 'What', 'How', 'Why', etc"
                    />
                  </div>
                </div>
              </div>
            </div>
            <div className="modal-footer">
              <button
                disabled={disable}
                className="btn-danger"
                onClick={closeModal}
              >
                Cancel
              </button>
              <button disabled={disable} className="btn" onClick={addQuestion}>
                Add Question
              </button>
            </div>
          </div>
        </div>
      );
    }
```

We have a state here `disable`. This state is used to toggle the disable state of the button in the UI.

Looking at the UI, we have two inputs. The first input holds the name of the user that will ask the question and the second input is where the question will be typed.

The `Add Question` will call the `addQuestion` function. This `addQuestion` function will get the values from the `user` and `question` inputs, it will use it to call the [http://localhost:1337/questions](http://localhost:1337/questions) endpoint. 

We passed the question and user texts as payload to the HTTP POST call, this creates the question in the Strapi backend.

## Create Question view

This page will display a specified question along with its answers and comments to the answers.

Let's create the files:

```
    mkdir pages/questions
    touch pages/questions/[id].js
```


Open `[id].js` and paste the below code:

```
    export default function Question() {
      const router = useRouter();
      const {
        query: { id },
      } = router;
    
      const [question, setQuestion] = useState();
      const [answers, setAnswers] = useState([]);
      const [showAnswerQuestionSection, setAnswerQuestionSection] = useState(false);
    
      useEffect(async () => {
        if (!id) return;
        const QuestionData = await axios.get(
          "http://localhost:1337/questions/" + id
        );
        var answersData = QuestionData?.data?.answers;
        setQuestion(QuestionData?.data);
        setAnswers(answersData);
      }, [id]);
    
      async function deleteQuestion() {
        if (confirm("Do you really want to delete this question?")) {
          await axios.delete("http://localhost:1337/questions/" + id);
          router.push("/");
        }
      }
    
      return (
        <div className={styles.questionviewcontainer}>
          <div className={styles.questionviewmain}>
            <div style={{ width: "100%" }}>
              <div className={styles.questionviewname}>
                <h1>{question?.qText}</h1>
              </div>
              <div className={styles.questionviewminidet}>
                <div style={{ display: "flex" }}>
                  <span>
                    <button
                      onClick={() => setAnswerQuestionSection((pV) => !pV)}
                      className="btn-danger"
                      style={{
                        backgroundColor: "unset",
                        color: "black",
                        border: "unset",
                        display: "flex",
                        alignItems: "center",
                        paddingLeft: "0",
                      }}
                    >
                      <AnswerIcon />
                      <span style={{ paddingLeft: "6px" }}>Answer</span>
                    </button>
                  </span>
                  <span>
                    <button
                      onClick={deleteQuestion}
                      className="btn-danger"
                      style={{
                        backgroundColor: "unset",
                        color: "black",
                        border: "unset",
                        display: "flex",
                        alignItems: "center",
                      }}
                    >
                      <DeleteIcon />
                      <span style={{ paddingLeft: "6px" }}>Delete</span>
                    </button>
                  </span>
                </div>
              </div>
              <div>
                {showAnswerQuestionSection ? (
                  <AnswerQuestionSection question={question} />
                ) : null}
              </div>
              <div className={styles.questionviewtransactionscont}>
                <div className={styles.questionviewtransactions}>
                  <h2>{answers?.length} Answers</h2>
                </div>
                <div
                  className={styles.questionviewtransactionslist}
                  style={{ padding: "unset" }}
                >
                  {!answers || answers?.length <= 0
                    ? "No Answers yet."
                    : answers
                        ?.sort((a, b) => b.created_at.localeCompare(a.created_at))
                        ?.map((answer, i) => <Answer key={i} answer={answer} />)}
                </div>
              </div>
            </div>
          </div>
        </div>
      );
    }
```


This component gets the id of the question from the router object. This id is used in the `useEffect` hook callback to call the endpoint `"http://localhost:1337/questions/" + id`. 

This will return the question with its details, the details will be stored in the `question` state. Then, the answers to the question will be stored in the `answers` state.
The UI displays the question details and the answers to the question. The comments of the answers are also all displayed.

The `deleteQuestion` function deletes the question from our Strapi backend. It calls the `"http://localhost:1337/questions/" + id` endpoint with the id of the question, the HTTP verb is DELETE which will delete the question from the backend.

The `AnswerQuestionSection` component is where the answer to the question is typed. This component is toggled by the `showAnswerQuestionSection` state, this state is manipulated by the `MsgIcon`. Let's see the code for this `AnswerQuestionSection` component:

```
    function AnswerQuestionSection({ question }) {
      var options = {
        placeholder: "Write your answer",
        readOnly: false,
        theme: "snow",
      };
      const editorRef = useRef();
      const userRef = useRef();
      const [disable, setDisable] = useState(false);
      const [q, setQuill] = useState();
    
      useEffect(() => {
        if (q) return;
        const _q = new Quill(editorRef.current, options);
        setQuill(_q);
      }, [q]);
    
      function answerQuestion() {
        setDisable(true);
        axios.post("http://localhost:1337/answers", {
          aText: q.getText(),
          user: userRef.current.value,
          question: question?.id,
        });
        setDisable(false);
        window.location.reload();
      }
    
      return (
        <div
          style={{
            marginTop: "16px",
            backgroundColor: "white",
          }}
        >
          <>
            <div>
              <input type="text" placeholder="Enter user here..." ref={userRef} />
            </div>
            <div ref={editorRef} style={{ backgroundColor: "white" }}></div>
            <div
              style={{
                display: "flex",
                justifyContent: "flex-end",
                padding: "14px",
              }}
            >
              <button
                style={{ borderRadius: "14px" }}
                onClick={answerQuestion}
                disabled={disable}
              >
                Post
              </button>
            </div>
          </>
        </div>
      );
    }
```

This component initializes a Quill editor in the `div[name="editor"]`. We have an input that collects the name of the user answering the question. The `Post` button will call the `answerQuestion` function when clicked. This `answerQuestion` function will call the endpoint `http://localhost:1337/answers`. The answer text, user name, and the question id are sent to the endpoint call as payload. The page is reloaded to reflect the new additions.

Let's code the `Answer` component:

```
    function Answer({ answer }) {
      const { aText, user, id } = answer;
      const [comments, setComments] = useState([]);
      const [showCommentInput, setShowCommentInput] = useState(false);
      const commentRef = useRef();
      const userRef = useRef();
    
      useEffect(async () => {
        const data = await axios.get("http://localhost:1337/comments/");
        const _comments = data?.data?.filter(
          (comment) => comment?.answer?.id == id
        );
        setComments(_comments);
      }, []);
    
      async function addComment() {
        const resultData = await axios.post("http://localhost:1337/comments/", {
          cText: commentRef.current.value,
          user: userRef.current.value,
          answer: id,
        });
        // set the comments to the comments state
    
        setComments((pV) => [...pV, resultData?.data]);
        userRef.current.value = "";
        commentRef.current.value = "";
        setShowCommentInput(false);
      }
    
      return (
        <div
          className={styles.question}
          style={{
            borderBottom: "1px solid rgba(234, 238, 243, 1)",
            padding: "14px",
          }}
        >
          <div className={styles.questiondetails}>
            <div
              style={{
                display: "flex",
                alignItems: "center",
              }}
            >
              <span
                style={{
                  display: "block",
                  width: "35px",
                  height: "35px",
                  backgroundColor: "grey",
                  borderRadius: "50%",
                }}
              ></span>
              <span style={{ paddingLeft: "4px" }}>{user}</span>
            </div>
            <div
              className={styles.questionbalance}
              style={{
                cursor: "pointer",
                paddingTop: "24px",
                paddingBottom: "24px",
              }}
            >
              <span>{aText}</span>
            </div>
            <div
              style={{
                display: "flex",
                alignItems: "center",
                color: "grey",
                cursor: "pointer",
              }}
              onClick={() => setShowCommentInput((pV) => !pV)}
            >
              <MsgIcon />
              <span style={{ paddingLeft: "6px" }}>{comments?.length}</span>
            </div>
            <div>
              {showCommentInput ? (
                <div
                  style={{
                    display: "flex",
                    flexDirection: "row",
                    alignItems: "center",
                    marginTop: "9px",
                  }}
                >
                  <input
                    type="text"
                    placeholder="Enter user..."
                    style={{
                      borderRadius: "9px",
                      width: "110px",
                      marginRight: "3px",
                    }}
                    ref={userRef}
                  />
                  <input
                    type="text"
                    placeholder="Add comment..."
                    style={{ borderRadius: "9px" }}
                    ref={commentRef}
                  />
                  <button
                    style={{
                      borderRadius: "19px",
                      fontSize: "14px",
                      fontWeight: "bolder",
                      boxSizing: "content-box",
                    }}
                    onClick={addComment}
                  >
                    <div style={{ display: "flex", whiteSpace: "nowrap" }}>
                      Add Comment
                    </div>
                  </button>
                </div>
              ) : null}
            </div>
            <div
              style={{
                paddingTop: "14px",
                marginLeft: "23px",
                marginBottom: "14px",
              }}
            >
              {comments
                ?.sort((a, b) => b.created_at.localeCompare(a.created_at))
                .map((comment, i) => (
                  <Comment key={i} comment={comment} />
                ))}
            </div>
          </div>
        </div>
      );
    }
```


This component is passed an `answer` object in its props object args. It uses the id from the `answer` object to fetch the comments connected to the answer. The comments are stored in the `comments` state

We have a `showCommentInput` state to toggles a UI section. This section is where users can comment on an answer. This section is opened when the `MsgIcon` beneath each answer is clicked. The comment section has two inputs that hold the commenter's name and the comment text, then the `Add Comment` button adds the comment to the answer. 

This `Add Comment` button calls the `addComment` function. This `addFunction` function makes an HTTP POST call the `http://localhost:1337/comments/` endpoint, the comment text, user name, and the answer id is sent also as the POST payload. This activity creates a new comment in our Comment collection.

The comments in the answer are rendered in this component, each comment is handled by the `Comment` component. Let's code the component:

```
    function Comment({ comment }) {
      const { user, cText } = comment;
      return (
        <div
          className={styles.question}
          style={{
            backgroundColor: "rgba(234, 238, 243, 1)",
            padding: "14px",
            marginBottom: "4px",
          }}
        >
          <div className={styles.questiondetails}>
            <div
              style={{
                display: "flex",
                alignItems: "center",
              }}
            >
              <span
                style={{
                  display: "block",
                  width: "35px",
                  height: "35px",
                  backgroundColor: "grey",
                  borderRadius: "50%",
                }}
              ></span>
              <span style={{ paddingLeft: "4px" }}>{user}</span>
            </div>
            <div
              className={styles.questionbalance}
              style={{
                cursor: "pointer",
                paddingTop: "14px",
                paddingBottom: "14px",
              }}
            >
              <span>{cText}</span>
            </div>
          </div>
        </div>
      );
    }
```

This component accepts a `comment` object in its props arg. This `comment` object contains the comment of an answer to render. The commenter and the comment text is destructured from the `comment` object. These are then displayed in the UI.

## Test app

**Add new question**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211022488_Screenshot+2021-09-09+at+19.09.40.png)
![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211022513_Screenshot+2021-09-09+at+19.09.48.png)


**View a question**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211059622_Screenshot+2021-09-09+at+19.10.46.png)


**Answer a question**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211160184_Screenshot+2021-09-09+at+19.12.21.png)
![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211160205_Screenshot+2021-09-09+at+19.12.28.png)


**Comment on an answer**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211202450_Screenshot+2021-09-09+at+19.13.00.png)
![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211202472_Screenshot+2021-09-09+at+19.13.06.png)


**Delete a question**

![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211234654_Screenshot+2021-09-09+at+19.13.37.png)
![](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211234674_Screenshot+2021-09-09+at+19.13.43.png)

# Source code

Find the source code of the project below:

- [Frontend](https://github.com/strapi-prjs/qa-front)
- [Backend](https://github.com/strapi-prjs/qa-app)
# Conclusion

We covered a lot in this tutorial. We started by introducing relationships in the database and going through the types of relationships one after the other explaining what they are.
Next, we saw how we can set up a Strapi project and how we can establish relations in Strapi collections.
Finally, we built a Q/A app just like Quora to fully demonstrate how relations in Strapi can be used in a real-life app.

This article is a goldmine of Strapi info.