Understanding and using Relations in Strapi
Tags: javascript, strapi, relations
Published: 2022-05-16 14:47:28


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. 

## What is Strapi?

[Strapi](https://strapi.io/) is an open-source Node.js headless CMS [](https://strapi.io/)(Content Management System) based on Node.js used to develop APIs(RESTful and GraphQL APIs) and build the APIs content. The APIs in Strapi are 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 from the endpoint.
- `blog` **POST**: This will create a new blog post from the endpoint.
- `blog/:id` **GET**: This will return the blog post with the id `:id`.
- `blog/:id` **DELETE**: This will delete the blog post with the id `:id` from the endpoint.

**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

The 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.

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. We can fetch a student's 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), 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. 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.

Now a `Company` table can point to many employee 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**.

### Many-to-Many
This 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**. 


### 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 relations set in Strapi?
Relationship links can be set in the Admin panel and manually from the generated Strapi project.


1. **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:

![Content Type builder modal showing field types](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651562045714_image.png)


Another UI is displayed in the modal:

![User relation field modal](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651562077417_image.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.

![Question content type relation modal](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651570912477_image.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.

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

![one-way relation](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651564367357_image.png)


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


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

![one-to-one relation](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651564394981_image.png)


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


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

![one-to-may relation](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651564414701_image.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`.

![many-to-one relation](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651568844661_image.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`.

![many-to-many relation](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651568886195_image.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`.

![many-way relation](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651568919920_image.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.

**2. Via Strapi Project**

Let's see how we set relations in our content types from our Strapi project. The content types in a Strapi project are stored in the `./src/api/` folder in our Strapi project. The relations are set in the `./src/api/[NAME]/content-types/[NAME]/schema.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’ll create a new property in the `attributes` property. Let's say we want to set a `one-to-one` between a `Student` model and a `Student-info` model, we will open the `./src/api/student/content-types/student/schema.json` file and add the code:

```js
    {
      "kind": "collectionType",
      "collectionName": "students",
      "info": {
        "singularName": "student",
        "pluralName": "students",
        "displayName": "Student",
        "description": ""
      },
      "options": {
        "draftAndPublish": true
      },
      "pluginOptions": {},
    
      // The fields are configured here
      "attributes": {
        "name": {
          "type": "string"
        },
    
        "student_info": { //field name
          "type": "relation", // field type
          "relation": "oneToOne", // relation type
          "target": "api::student-info.student-info", // the target of the relation
          "inversedBy": "student" // more info here - https://docs.strapi.io/developer-docs/latest/development/backend-customization/models.html#relations
        }
      }
    }
```
The relation field is `student_info`. The `model` refers to the content type in Strapi the field is pointing to. It is set to `student_info` and so this property in the `Student` content type points to the `student_info` content type.

We set the `type` as ``relation`` and the `relation` as `oneToOne`. All these state that the `Student` model has and belongs to one `StudentInfo`.

Let's see inside `./src/api/student-info/content-types/student-info/schema.json` file

```js
    {
      "kind": "collectionType",
      "collectionName": "student_infos",
      "info": {
        "singularName": "student-info",
        "pluralName": "student-infos",
        "displayName": "studentInfo"
      },
      "options": {
        "draftAndPublish": true
      },
      "pluginOptions": {},
      "attributes": {
        "bio": {
          "type": "text"
        },
        "student": {
          "type": "relation",
          "relation": "oneToOne",
          "target": "api::student.student",
          "inversedBy": "student_info"
        }
      }
    }
```
Here, we have a `student` property which points to the `student` collection type. The `relation` set here is also `oneToOne`

These two JSON configs of both `Student` and `StudentInfo` models establish a one-to-one relationship between them as you can see in the interface below. This is similar for all other relations.


![Content Type builder modal showing field types](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651572633689_image.png)


### One-to-Many
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 `/schema.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 `./src/api/company/content-types/company/schema.json` file.

```js
    {
    ...  
    "attributes": {
        "name": {
          "type": "string"
        },
        "employees": {
          "type": "relation",
          "relation": "oneToMany",
          "target": "api::employee.employee",
          "mappedBy": "company"
        }
      }
    }
```    

Also, in `./src/api/employee/content-types/employee/schema.json` file:

```js
    {
    ...  
      "attributes": {
        "name": {
          "type": "string"
        },
        "company": {
          "type": "relation",
          "relation": "manyToOne",
          "target": "api::company.company",
          "inversedBy": "employees"
        }
      }
    }
```
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  relation field 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 in `./src/api/doctor/content-types/doctor/schema.json` will be this:

```js
    {
      ...
      "attributes": {
        "name": {
          "type": "string"
        },
        "hospitals": {
          "type": "relation",
          "relation": "manyToMany",
          "target": "api::hospital.hospital",
          "inversedBy": "doctors"
        }
      }
    }
```
The `hospital` relation field points to many hospitals. 

The `Hospital` model will be this:
`./src/api/hospital/content-types/hospital/schema.json`:

```js
    {
      ...
      "attributes": {
        "name": {
          "type": "string"
        },
        "doctors": {
          "type": "relation",
          "relation": "manyToMany",
          "target": "api::doctor.doctor",
          "inversedBy": "hospitals"
        }
      }
    }
```
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  `/schema.json` file only. The other model will have no relation connecting to other model define in its `/schema.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:

```js
    {
      ...  
      "attributes": {
        "name": {
          "type": "string"
        },
        "details": {
          "type": "relation",
          "relation": "oneToOne", 
          "target": "api::detail.detail"
        }
      }
    }
```

There will be no relation setting in the `Detail` schema 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`:

```js
    {
      ...  
      "attributes": {
        "name": {
          "type": "string"
        },
        "cars": {
          "type": "relation",
          "relation": "oneToMany",
          "target": "api::car.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.

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 QnA app just like Quora, and users can ask questions, answer questions, and comment on answers. We will build this app so as 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 both backend and frontend projects:

```bash
    mkdir relations
```
We move into the folder: 

```bash
    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.

To start the project, run:

```bash
    yarn develop
```

Strapi will serve the project on `localhost:1337`. It will launch the Strapi admin UI panel on `localhost:1337/admin`.


![Strapi admin sign up page](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651561934870_image.png)


Fill in your details and click on the **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 QnA app. We will have `Question`, `Answer` and `Comment`.
Our `Question` model will be this:

```js
    Question {
        qText
        user
    }
```

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


![Strapi Question content type](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651562248677_image.png)


The `Answer` model will be this:

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

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

![Answer content type](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651585780225_image.png)


The `Comment` model will look like this:

```js
    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.


![Comment content type](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651563295045_image.png)


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.

![Question model - qText field](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651562762172_image.png)

- Click on `+ Add another field`.
- Select `text`.
- Type in `user`.

![Question model - user field](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651562807835_image.png)

- 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`.

![Answer model - aText field](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651562691075_image.png)

- 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.

![Answer model - question one-to-many relation field](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651562635378_image.png)

- 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`.

![Comment content type display name](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651563176630_image.png)

- Click on the `+ Add another field` button.
- Select `text` field.
- Type in `cText` and click on the `+ Add another field` button.

![Comment model - cText field](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651563226116_image.png)

- 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` but not from `Answer` to comment. So, the `comments` field will not appear on the `Answer` response.

![Comment relation field relations](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651586584295_image.png)

- 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.

Before we start building the frontend, we have set the permissions for a **Public** unauthenticated user so that our Strapi API can return data from routes without authentication.


> **NOTE:** You’d typically need authentication in your application, especially when dealing with  `create`, `delete` and `update` endpoints


![Enable all permissions for answer collection type](https://paper-attachments.dropbox.com/s_7E0CDF98C8224A710ADADF2AFD2360BE291C4097D3580E7A564BF1A5D07D603A_1651570526604_image.png)



## Building the QnA 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**

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


**Adding new question**

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


**Answering a question**

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


**Commenting on an answer**

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


**Deleting a question**

![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**

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

![Viewing all answers and comments](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: 

```bash
    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:

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

```js
    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:

```bash
    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:

```js
    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.

```js
    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:

```js
    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 const getServerSideProps = async () => {
      const { data } = await axios.get("http://localhost:1337/api/questions?populate=*");
      console.log(data);
      return {
        props: {
          the_questions: data.data
        }
      }
    }
    export default function Home({ the_questions }) {
      const [showAddQuestionModal, setShowAddQuestionModal] = useState(false);
      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>
                {the_questions?.map((question) => (
                  <QuestionCard key={question.id} question={question} />
                ))}
              </div>
            </div>
            {showAddQuestionModal ? (
              <AddQuestionDialog
                closeModal={() => setShowAddQuestionModal((pV) => !pV)}
              />
            ) : null}
          </main>
        </div>
      );
    }
```
Here, we’re using `getServerSideProps` to fetch our questions with `axios` from the `[http://localhost:1337/api/questions?populate=*](http://localhost:1337/questions)` endpoint. We return the questions as `the_questions` from the `getServerSideProps` in the `props` object, which we can access in `Home` component.


> **Note:**  The **`populate=**` query parameter allows us to fetch all fields relation, media and components fields 

We have the `showAddQuestionModal` state. The `showAddQuestionModal` state is used to toggle the display of the `AddQuestionsModal` on and off.

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 from `the_questions` prop are rendered. Each question in the `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.

```bash
    mkdir components/QuestionCard
    touch components/QuestionCard/index.js
    touch components/QuestionCard/QuestionCard.module.css
```
Open the `index.js` and paste the below code:

```js
    import styles from "./QuestionCard.module.css";
    import Link from "next/link";
    export default function QuestionCard({ question }) {
      const { id } = question
      const { qText, user, answers } = question.attributes;
      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:

```bash
    mkdir components/AddQuestionDialog
    touch components/AddQuestionDialog/index.js
```
Open the `index.js` and paste the below code:

```js
    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/api/questions", {
          data: {
            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:

```js
    mkdir pages/questions
    touch pages/questions/[id].js
```
Open `[id].js` and paste the below code:

```js
    import styles from "../../styles/QuestionView.module.css";
    import { useRouter } from "next/router";
    import axios from "axios";
    import { useEffect, useState, useRef } from "react";
    import { MsgIcon } from "../../components/QuestionCard";
    
    export const getServerSideProps = async ({ params }) => {
      const { id } = params
      const question = await axios.get(`http://localhost:1337/api/questions/${id}?populate=*`);
      const { data: { attributes: { answers } } } = question.data
      const comments = await axios.get(`http://localhost:1337/api/comments?populate=*`);
      console.log(question);
      console.log(answers);
      return {
        props: {
          id,
          question: question.data.data,
          answers: answers.data,
          comments: comments.data.data
        }
      }
    }
    export default function Question({ id, question, answers, comments }) {
      const router = useRouter();
      const [showAnswerQuestionSection, setAnswerQuestionSection] = useState(false);
      async function deleteQuestion() {
        if (confirm("Do you really want to delete this question?")) {
          await axios.delete(`http://localhost:1337/api/questions/${id}`);
          router.push("/");
        }
      }
      return (
        <div className={styles.questionviewcontainer}>
          <div className={styles.questionviewmain}>
            <div style={{ width: "100%" }}>
              <div className={styles.questionviewname}>
                <h1>{question?.attributes.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?.map((answer, i) => <Answer key={answer.id} answer={answer} comments={comments} />)}
                </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/api/questions/${id}`.
This will return the question with its details.

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/api/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:

```js
    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/api/answers", {
          data: {
            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
              name="editor"
              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:

```js
    function Answer({ answer, comments }) {
      const { id } = answer
      const { aText, user } = answer.attributes;
      console.log({ comments });
      const [_comments, setComments] = useState(comments ? comments.filter(comment => comment.attributes.answer.data?.id == id) : []);
      console.log(id, comments);
      const [showCommentInput, setShowCommentInput] = useState(false);
      const commentRef = useRef();
      const userRef = useRef();
      async function addComment() {
        const resultData = await axios.post("http://localhost:1337/api/comments", {
          data: {
            cText: commentRef.current.value,
            user: userRef.current.value,
            answer: id,
          }
        });
        setShowCommentInput(false);
        window.location.reload();
      }
      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?.map((comment) => (
                <Comment key={comment.id} 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 filter all the fetched comments connected to the answer. 

We have a `showCommentInput` state to toggles a UI section. This section is where users can comment on an answer. This section opens 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 to the `http://localhost:1337/api/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:

```js
    function Comment({ comment }) {
      const { user, cText } = comment.attributes;
      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 de-structured from the `comment` object. These are then displayed in the UI.

## Test the App

**Add new question:**

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



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


**View a question:**

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


**Answer a question:**

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



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


**Comment on an answer:**

![Comment on an answer](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211202450_Screenshot+2021-09-09+at+19.13.00.png)



![comment to answer submitted](https://paper-attachments.dropbox.com/s_2EDBBD97A87BD2A68DAD0875F695EEA8BB056667FDA2305DA12AF400715264AD_1631211202472_Screenshot+2021-09-09+at+19.13.06.png)


**Delete a question:**

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



![Question deleted](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/miracleonyenma/strapi-qa-v4)
- [Backend](https://github.com/miracleonyenma/strapi-qa-api-v4)

## 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 QnA 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.