How would you inject links into rich text content given a particular data structure?

System Information
  • Strapi Version: 3.6.8
  • Operating System: Windows
  • Database: SQLite
  • Node Version: 12.18.4
  • NPM Version: 6.14.11
  • Yarn Version: 1.22.10

I’m working on a NextJS project using Strapi and I’m trying to work out the best way to design a feature for it. So I’m going to put forward what the feature is and then ramble a bit on different ways I have thought of to solve it, each with their own positives and negatives. I’d really appreciate it if anyone could advise me on the best option to go for based on efficiency and user experience of back-end users, I am quite new to database development in this way so I’m sure I will have forgotten or overlooked something important.

So, the website is a technical manual of sorts with chapters, subsections and exercises. Here’s a breakdown of how the website is structured:

  • Chapters are parents of subsections and subsections are parents of exercises.
  • Each subsection and exercise is distinguised by a unique letter represented on the front-end in square brackets (e.g. [A], [F]).
  • Subsections and exercise unique letters are not separate, i.e. you cannot have subsection [F] and exercise [F], if subsection [F] exists and the next piece of context to be created is an exercise, it has the unique letter [G].
  • When we run out of letters, we follow the pattern [AA], [AB] … [BA] etc.
  • Each piece of content on the front end (be that a chapter, subsection or exercise) is made up of a few rich text boxes and then a relational list of it’s children (if it has any).
  • The rich text content have links to other subsections and exercises.
  • Each chapter, subsection and exercise is a page on the front-end, whose link is generated as a hyphenated slug of the content’s name field, e.g.

The Problem: I want users of the back-end to be able to add a link to another page just by putting the unique letter for that subsection or exercise as the URL. Rather than having to get the slug for that page and copy-paste it into the rich-text box every time.

By that I mean I want them to be able to write the URLs like so:

Lorem ipsum dolor sit amet, [consectetur adipiscing elit](/C). Donec [sodales turpis](/K) nec tortor tristique bibendum.

I’ve thought of a few different ways to achieve this but they all have their drawbacks. I’m not saying these are the only ways, I’m sure more experienced devs will have better designs and I’d be very appreciative to hear them but here goes.

1. Use the beforeCreate and beforeUpdate hooks.

Use the before... hooks on the models for chapter, subsection and exercise to call a method that goes through the content and uses RegEx looks for any patterns that match /[A-Z][A-Z], then uses findOne on subsection and exercise to grab it’s slug and replace the /[A-Z][A-Z] string before the content is saved to the database.

This seems like quite a nice, clean way to do it that is immediately obvious to the back-end user because after they click “Save” they’ll be able to see all the links populate.

However, the drawback is that if they then go and change the name of any of the subsections/exercises referenced in that now-saved rich text box, it’s slug will change and therefore the links will be broken.

2. In addition to implementing (1.), run the same RegEx function but on all content in the database anytime a subsection or exercise’s name (and therefore it’s slug) is updated.

This would solve the issue of old urls breaking but at some point this must take it’s toll once we have lots and lots of data in the database, given we’d be looping over every single exercise and subsection and finding every single exercise and subsection referenced in each rich text box.

3. Leave the DB with the /[A-Z][A-Z] links in the rich text content and populate them when the endpoint is requested.

Given the project is built on NextJS, this extra processing before serving up the data wouldn’t matter because the pages are statically built server-side.

However, the downside is that this sort of happens “in the background” and back-end users aren’t being shown what is actually served from the API compared to what they’ve written in the rich text boxes (the URLs will still be /[A-Z][A-Z] in the rich text boxes). This might be a minor issue but having those two things out of sync feels a bit strange.

4. Leave the DB with the /[A-Z][A-Z] links and populate the links on the front-end with additional API calls.

This just hands off the problem to the front-end entirely and is like the API just saying “here’s what was added to my content by the back-end user, do whatever you like with it”. So essentially, the front-end gets the content full of (/C), (/K), (/BF) links for example and it’s up to the front-end to make additional API calls to find the slug of the content with those unique letters.

I sort of like this one better than (3.) because there isn’t anything happening in the background that causes what it served by the API to be different to what is actually saved in the database. Again, the front-end is built using NextJS so all the additional API calls don’t really matter in terms of speed but it still feels like a bit of a lazy way of doing it given I’ve got total control over the API, yet I’m just kicking the issue down the road until the front-end has to deal with it.

Okay so that’s my dilemma, I don’t know which of those tradeoffs is better than each other, which is best practice when it comes to API design, or if there’s a 5th or 6th possibility out there that I haven’t even thought of!

What would you do in this situation? Which has the best experience for back-end users but also is efficient? Would you solve it another way entirely?

Thank you to anybody and everybody that has taken the time to read all this, I’m sorry that this turned it to quite a long question! Any comments/advice whatsoever is greatly appreciated so I’ll say thank you in advance!