Need Help: How to add (if not exists) related records in POST Method

Hi everyone. Happy new forum!

Git repo : git clone https://github.com/slaaavi/strapiTagsExample.git

Strapi application

Strapi v3.1.6 created with --quickstart (using SQLite)

The Data Model is:

3 Tables - Posts, Categories and Tags.

Posts has and belongs to many Categories |
Posts has and belongs to many Tags

In ./tmp.data.db has already example data (few posts,tags and categories)

What I want to achieve

Adding (if not exists) related records in POST Method.

When I add a new post with categories and tags, if some of the category or tag does not already exists in db - to be added.

Exact Example:

POST method to http://localhost:1337/posts with body:

    {
        "uid": 1111,
        "title": "New article title created by Strapi REST API endpoint",
        "content": "New article content created by Strapi REST API endpoint with POST method...",
        "categories": [
            "Health",
            "NewCat1",
            "NewCat2"
        ],
        "tags": [
            "brain",
            "newTag1",
            "newTag2"
        ]
    }

Current response:

{
    "id": 5,
    "title": "New article title created by Strapi REST API endpoint",
    "content": "New article content created by Strapi REST API endpoint with POST method...",
    "slug": null,
    "uid": 1111,
    "created_by": null,
    "updated_by": null,
    "created_at": "2020-10-02T07:16:44.581Z",
    "updated_at": "2020-10-02T07:16:44.581Z",
    "categories": [],
    "tags": []
}

What I want to be the response:

{
    "id": 5,
    "title": "New article title created by Strapi REST API endpoint",
    "content": "New article content created by Strapi REST API endpoint with POST method...",
    "slug": null,
    "uid": 1111,
    "created_by": null,
    "updated_by": null,
    "created_at": "2020-10-02T07:16:44.581Z",
    "updated_at": "2020-10-02T07:16:44.581Z",
    "categories": [
            {
                "id": 3,
                "name": "Health",
                "slug": "health"
            },
                     {
                "id": 6,
                "name": "NewCat1",
                "slug": "newcat1"
            },
                     {
                "id": 7,
                "name": "NewCat2",
                "slug": "newcat2"
            }
     "tags": [
            {
                "id": 7,
                "name": "brain",
                "slug": "brain",
                "created_by": 1,
                "updated_by": 1,
                "created_at": "2020-10-02T05:51:48.013Z",
                "updated_at": "2020-10-02T05:51:48.022Z"
            },
                       {
                "id": 9,
                "name": "newTag1",
                "slug": "newtag1",
                "created_by": 1,
                "updated_by": 1,
                "created_at": "2020-10-02T05:51:48.013Z",
                "updated_at": "2020-10-02T05:51:48.022Z"
            },           {
                "id": 10,
                "name": "newTag2",
                "slug": "newtag1",
                "created_by": 1,
                "updated_by": 1,
                "created_at": "2020-10-02T05:51:48.013Z",
                "updated_at": "2020-10-02T05:51:48.022Z"
            }
        ]
}

“Health” category already exists, that’s why the ID is 3. “NewCat1” and “NewCat1” doesn’t not exists, so they were added in db and got new IDs 6 and 7.
Same as the tags.

If I understand right after making the post request, Strapi in the backend first needs to find the IDs of each Category and Post. If they don’t exists - to add them and get back all the IDs of the added categories and tags and then add do the post record like this:

{
    "title": "New article title created by Strapi REST API endpoint",
    "content": "New article content created by Strapi REST API endpoint with POST method...",
    "categories": [3,6,7],
    "tags": [7,9,10]
}

I’ve tried with weeks/months to find information (youtube, stackoverflow, github, udemy…), but for my surprise I was not able to find anything showing how to do exactly this. Doesn’t matter if it’s about Strapi,Feathers,Prisma… If anyone can show me the code how to do this will be huge help for me and other beginners like me.

Thank you.

you will need to customize the default create controller (can be found here: https://strapi.io/documentation/v3.x/concepts/controllers.html#create )

Effectively you will need to loop through the relational data you want to create, check if it exists, else create it and construct an array of IDs to pass along to the create service for the post model.

We don’t by default do this as it’s generally expected you handle this on your frontend side.

@DMehaffy : Thanks for the reply.

I think I understand your explanation, but I don’t know how to write it(not to mention efficiently).
If it’s going to take 10-20 lines of code will be in great help to have it also as reference how to do similar actions in the future( when starting implementing more relationships). I guess what I’m trying to do is quite common case, but as I’m still learning, I never did it and I was hoping for example(with code) to help me understand.

If it’s too much to ask for someone to write the code for my simple example, then if anyone has this or similar case already implemented in his Strapi project (customized default create controller), please share.

Thank you

So this isn’t a perfect example but just a rough one…

What I’m doing is overwriting the create controller from the model article which has a manyWay relation to a model called tag. The goal here is to allow posting new/existing tags as strings OR the ID of the tag when you are creating a new article.

It will take the array of tags, if it’s an ID (based on SQL, won’t work for mongoose) just add that to the array of IDs. Else if they are strings, it will query the tag model to see if they exist and if not create them. In either case here, it’s adding the ID to the array and overwriting the ctx.request.body.tags with the new array of IDs.

This isn’t a perfect example and if you are planning to upload media at the same time it will break because it will try to pass strings as IDs, but other than that for normal application/json POST requests this should work.

Path: ./api/article/controllers/article.settings.json

'use strict';
const { parseMultipartData, sanitizeEntity } = require('strapi-utils');

module.exports = {
  /**
   * Create a record.
   *
   * @return {Object}
   */

  async create(ctx) {
    let newArticle = ctx.request.body
    let entity;
    let tags = [];

    // Loop through tags passed as strings or an existing ID
    if (newArticle.tags.length > 0) {
      for (let i = 0; i < newArticle.tags.length; i++) {
        // Check if tag is an ID or a new one via string
        if (typeof(newArticle.tags[i]) === 'number') {
          tags.push(newArticle.tags[i])
        } else {
          // Check if the tag already exists
          let check = await strapi.query('tag').find({ name: newArticle.tags[i]})

          // If it exists, push that id into an array, else create it and push the new ID
          if (check.length > 0 && check[0].name === newArticle.tags[i]) {
            tags.push(check[0].id)
          } else {
            let newTag = await strapi.query('tag').create({ name: newArticle.tags[i]})
            tags.push(newTag.id)
          }
        }
      }
      // Overwrite the ctx.request.body.tags with the new integer based array
      newArticle.tags = tags
    }



    if (ctx.is('multipart')) {
      // need to adjust this for the tags, right now it will break
      const { data, files } = parseMultipartData(ctx);
      entity = await strapi.services.article.create(data, { files });
    } else {
      // pass the new request body with the proper array of IDs and create the article
      entity = await strapi.services.article.create(newArticle);
    }
    return sanitizeEntity(entity, { model: strapi.models.article });
  },
};

The example may not be perfect, you could probably swap out the for loop with a map, but this was a quick and dirty example. Please feel free to adjust my terrible JS :laughing:

Thank you so much, Derrick!!! Exactly what I needed! :slight_smile:

With your example now I can start using Strapi and learn it in practice.

Have an amazing weekend!

Glad I could help, and you as well. :slight_smile:

Writing JS in json file article.settings.json ?

That was a typo, it should be article.js not article.settings.json :see_no_evil: