Using Notion as a Content Management System (CMS) for your applications

Recently while developing GarajApp, a car magazine app, we had a need to use a CMS to add content such as News, Campaigns and static pages like Guides. We considered several options like Strapi, Contentful or even implementing a simple enough markdown editor with WYSIWYG editor.

Choosing the right CMS

Founder of GarajApp tried using Strapi earlier and it ended up a maintenance burden with updates, errors, and not-so-easy development flow. We needed something simple enough, yet production ready. We just needed to serve some content from our database to the mobile app, how hard it could be?

We were leaning towards implementing a very simple page with a good WYSIWYG editor, perhaps with markdown support, that simply retrieves a page record and updates when we click save. It really looked easy but we had some concerns like adding a login, uploading the photos, managing the pages etc. Ok, so it wasn't so easy, then should we go with a full-scale content management system that supports a looot of things?

During a weekly brainstorming session, we threw the idea of using Notion! Notion has a great user experience when you edit or organize content, it has great permissions and login capabilities, and uploading images is so easy on that. We immediately started doing an end-to-end proof of concept, editing a page in Notion should be reflected in our mobile app within 10 minutes with using GraphQL(with Hasura engine).



Proof of Concept

We first created a page in Notion for News and an example item in it with adding a few columns.


Notion has an API that gives the list of pages and the page content. We used Notion client for JavaScript.

Now we need to run this script and call the API every 10 minutes with cron, can be more frequent if needed, and update the records in the database. Simply upserting the objects in fetchedNews to our news table in our database would be enough. We used Hasura as a backend in this project, so we just called the API with an upsert request with conflict on id field.

Making a GraphQL call to our Hasura engine, and we return the data! Now you can implement a code for transforming this Notion blocks to be displayable elements. You can implement a simple markdown generator or can just directly use the blocks in your code. We moved with implementing a module in iOS that converts these blocks into a display.

Handling the Images

Here's the tricky part, image blocks are returning us URLs that expires. So, if we copy the blocks from Notion directly into our database, our users won't be able to see the images after 60 minutes. To solve this issue, we simply cloned the images to Cloudinary during importing our Notion pages and saving the Cloudinary URL into our database. This gave also a power over the images for us, so that we could implement different versions of the same images easily.

Production ready implementation

To make this implementation production ready, we created 2 main pages like GarajDataStaging and GarajDataProduction. GarajDataProduction's data structure(columns and so on) is changed less frequently and controlled while doing any modification not to have any surprises. We also added Sentry to our script to make sure that our code is not failing. We haven't implemented any monitoring system for the code as the scope was too small, but feel free to include that, too.


Conclusion

It has been a great experience to use Notion as CMS, it's free and it has a great user experience. Simplifying the content management enabled us to develop the application smoothly, we were able to get real data during the development thanks to ease of using Notion. Not to forget to mention, Notion's blocks system gives a powerful yet organizable content with a great UI for modifying the content, which makes it a default choice over using HTML, markdown or a custom implementation.

Popular posts from this blog

Linode, Firewall, Cloudflare Full (Strict) with Terraform Cloud

How to Communicate Your Process Visually using BPMN as Code

Load Testing with Ruby-JMeter