Elevating Strapi: Notum’s Journey in Creating Three Open-Source Plugins

Discover Open-Source Plugins for Content Versioning, Record Locking, and Location Management, Enhancing Strapi Projects Globally — by Notum, a Strapi Implementation Partner

Strapi
Strapi
9 min readDec 8, 2023

--

Notum is an official Strapi implementation partner based in the Czech Republic creating strapi-powered projects for clients from all over the world. In our work on commercial projects, we discovered the strength of Strapi lies in its foundational flexibility. This allowed us to creatively extend its capabilities by developing custom plugins to suit our unique project needs.

We strongly believe that the Strapi community is one of the most important aspects of the Strapi ecosystem therefore we decided to make the three plugins we developed open-source. Now the content-versioning plugin, the record-locking plugin and the location plugin are available for everyone at the Strapi Market and in this blog post, we will explain how they can be helpful even for your Strapi project!

Content-versioning plugin

Even though Strapi excels in creating highly customizable data structures for your content it only allows you to keep one version of it. While having many content types it can be hard to keep track of all the updates especially if you also manage multiple translations of it.

This is exactly the problem one of our clients ran into and thus the idea to create a content-versioning plugin was born. Nowadays this is our most popular plugin attacking the milestone of 10,000 monthly downloads.

Utilizing this plugin you can have multiple draft versions of the same entity and time-travel between the updates just as they were created. And what about internationalization? The plugin works perfectly with how internationalization is managed within Strapi so whenever you switch to a different language you will only see the versions in the selected language.

Nonetheless, the plugin still has some limitations like the inability to support unique fields and relations inside components but we are frequently adding new features based on the feedback from the community.

How does it work under the hood?

This plugin is quite complicated and looking at the source code most developers will probably need some time to figure out the logic being used. It is also a nice challenge for senior developers who wish to perfect their Strapi skills. In a nutshell, the entities are still stored in the same database table but each of them is assigned a unique versioning identifier generated by the uuid library and a link to relevant locale.

That information is used to determine that it is not just another entity but a version of a previously created entity. The plugin also provides additional logic for switching between the versions, deleting them, and other standard processes.

While developing this plugin one of the most challenging parts derives from the fact that when the content has multiple locales and each locale has multiple versions they need to be properly linked together and those links need to be updated when some version is deleted or a new one is created.

We also wanted to provide users with the ability to create a new version of the content every time the save button is clicked. To achieve this we needed to create a patch for the Strapi Admin package and change the url of the API call that is performed when the save button is clicked to the url exposed by our plugin. However, patching the package was not convenient so we later solved this by using Strapi’s internal API — entity service decorator. In the most recent version users can enjoy this feature out of the box without having to go through the patching process.

In case you are hungry for more details check the source code.

Is this plugin doomed?

Although we are striving to make the plugin better with every update and there are features on the roadmap like the ability to update the current version without creating a new one does this all make sense in the light of Strapi V5 coming soon?

The upcoming release of Strapi is set to include a Draft and Publish feature, providing users with the capability to concurrently create both a draft and a published version of the same entry. Additionally, in the months to come, they plan to introduce content versioning features. Until these new features are rolled out and users make the transition to version 5, our plugin remains valuable for those still using version 4 or needing multi-draft support.

Record-locking plugin

Does your content team have more than one member? Then you definitely need this plugin!

Imagine a scenario where you work with your teammate on a shiny new page and your task is to select proper pictures for the content and your colleague is supposed to write the texts.

You found the right pictures and saved them in the entity from which the new page gets data and happily you started doing something else in a different tab. After a while you realised that one of the pictures should not be there so you came back and decided to remove it and hit the save button.

What you did not know is that your teammate meanwhile saved his work and you did not have the latest data if you had not refreshed the browser. Consequently, this leads to his work being rewritten with empty fields.

This way data can easily be lost forever if it is not backed up somewhere else and the more users your admin panel has the more likely this is to happen. That is why we created the record-locking plugin.

Notum Strapi

Whenever you open an entity that is already being edited by someone else the plugin tells you who the currently editing user is and will not let you use the content editor thus safeguarding the data from content editing.

This plugin has enjoyed a few hundred downloads for every of the past months but recently it has reached the mark of 1,000 monthly downloads and its popularity continues to rise.

Utilizing web socket in Strapi

The plugin features its own collection of currently edited entities which is essential for it to work. When a user opens an entity in the admin panel a component the plugin injects into the editor is mounted and checks whether the entity ID is present in the collection of opened entities. If so a modal is displayed saying that this entity is being edited by someone else.

If its ID is not present in the collection it sends a web socket event to the Strapi backend with the details of the opened entity. When the backend receives this event it adds this entity to the collection of opened entities.

const collectionType = useRouteMatch(
"/content-manager/collectionType/:slug/:id"
);
const singleType = useRouteMatch("/content-manager/singleType/:slug");

let params, statusUrl;
if (collectionType) {
params = collectionType.params;
statusUrl = `/record-locking/get-status/${params.id}/`;
} else {
params = singleType.params;
statusUrl = `/record-locking/get-status/`;
}
const { id, slug } = params;
statusUrl += slug;

const [isLocked, setIsLocked] = useState(false);
const [username, setUsername] = useState("");
const socket = useRef({});

const { id: userId } = auth.getUserInfo();
const lockingData = { entityId: id, entitySlug: slug, userId };

const attemptLocking = () => {
try {
request(statusUrl).then((response) => {
if (!response) {
socket.current?.emit("openEntity", lockingData);
} else {
setIsLocked(true);
setUsername(response.editedBy);
}
});
} catch (error) {
console.warn(error);
}
};

This is a part of the code (component) that is injected into Strapi admin and checks whether the entity is locked. If it is locked it displays the user who is currently editing and if it is not it locks the entity with information about the user.

Why did we use a web socket to achieve this? The same behaviour could be achieved via REST API but there is an edge case when the user just closes the browser. When this happens the web socket receives an event informing the connection was closed and on this event, the entity is removed from the collection of opened entities because otherwise it would be still locked.

socket.on("disconnect", async (data) => {
await strapi.db.query("plugin::record-locking.open-entity").deleteMany({
where: {
connectionId: socket.conn.id,
},
});
});

This snippet ensures that when the user closes the browser the entity does not remain locked.

Some plugins need just a little bit of love now and then

As this plugin’s utility is quite simple and sufficient for what it should accomplish there are just a few features on the roadmap but other than that we are mainly keeping its dependencies up to date and solving issues its users raise on the GitHub repository.

Currently, we have a feature request for the takeover button which would allow users with higher permissions to take over the control over the content editor from users with lower permissions.

Location plugin

Filtering data you can get as a response from the API is easy in Strapi. There are plenty of options and in the end, it all comes to just adding query parameters. However many projects also require filtering by geographical data — latitude and longitude coordinates.

This can be used to filter restaurants, shops, or anything that is in proximity to other geographical locations (usually this is the location of the user). As this was a must-have feature for one of our projects we decided to develop a location plugin. Later we found that this was the second most wanted plugin by the community!

By passing additional query parameters to the API call the plugin enables you to either filter entries within a distance of a specified location or find ones that are located on the exact coordinates provided.

Being our youngest plugin it is also the least popular one but it quickly rose from zero to hundreds of monthly downloads. Among recent updates, there is for example a feature to pick a location from the map using a drag-and-drop pin and there are plenty of features like supporting geographical areas and reverse geocoding on the roadmap! We also keep in mind that our plugins should preferably have no commercial dependencies so we used open source maps and no Google Maps API key is needed.

We also actively listen to user feedback and their needs regarding the usage of this plugin so feel free to play around with it and if something is missing for your use case, submit a feature request and help us shape the roadmap! Since Strapi is also constantly evolving and adding new features we will strive to maintain the plugin so it is always compatible with the latest features of Strapi and works effortlessly with the most recent versions.

Extending the database

The key element of this plugin is the PostGIS extension for the PostgreSQL database which enables it to perform geospatial queries and it also allows data to be saved in the format of geospatial points.

Once a user fills the coordinates using the custom field provided by the plugin and saves the entity they get converted to this format of geospatial points and are stored in an additional column with the _geom suffix added by the plugin.

When data is requested using the REST API the plugin’s middleware gets in the way and if parameters used for filtering the location are provided it performs a geospatial query and only returns those entities that match the criteria.

Contributors from the Strapi community

Even though as creators of these plugins we often have to do the heavy lifting while developing new features we still get tons of valuable feedback and great support from the community. Not only that but some folks even took the hands-on approach and submitted pull requests that have significantly helped to improve the plugins.

Working with developers from the Strapi community was a very joyful experience for us and we are convinced that we have found a fertile ground for our open-source initiatives.

Let’s make Strapi the best CMS ever

We believe that Strapi has the potential to become the best CMS there is thanks to its open-source nature and state-of-the-art technology. However, Strapi itself can’t cover every use case it might be utilized for. As far as we are concerned this can be overcome by a large ecosystem of plugins and there are already over 160 of them available at the Strapi market!

With that in mind, we would like to encourage every Strapi enthusiast to join us on the journey of developing Strapi plugins. Everyone can start with small contributions like updating the readme or you can always take a bigger step and create your own plugin.

Notum is an exemplary proof that even while working on commercial projects open-source can be created. Strapi has helped us to build amazing projects so let’s help it back.

--

--

Strapi
Strapi

The open source Headless CMS Front-End Developers love.