Ember CMS (MVP)

fast, headless & versatile CMS

type:

Personal project

stack:

Next.js, Firebase, GraphQL (Apollo server), Typescript

what i did:

Everything

Your special Sound

project purpose & problems to solve

Fast & flexible to use, easy to extend

This project is arguably the largest project I've built as a web dev. Since I didn't plan on releasing this as a product or service for anyone but myself anytime soon I had two primary goals for this project:

  1. Create a CMS that I would enjoy using myself for this portfolio but also for any future websites I would want to build.
  2. Create as much of what I need for it myself from scratch as a learning excercise and to decrease reliance on external libraries in the future.
  3. I wanted to use Firebase as a database solution as well as take advantage of their handy API's for user authentication. This way I could get even more comfortable with the Firebase and Google Cloud platform.

I had a range of features I wanted this CMS to have, but considering the scope and size of this project and me just being one person, I wanted to first create a Minimum Viable Product (MVP) where i build all the essential features that I need for me to start using it seriously. As such, this project is not completely finished and there are things missing and incomplete, but it has reached a point where it's fully usable for me in production. I defined the following feature list for this project:

This page would be way too long if I cover every feature in detail so instead I'll detail the most important ones below and how I overcame some hurdles and the lessons I've learned building them.

stack & structure

Firebase & Google Cloud Backend,REST & GraphQL API + Next.Js Frontend.

I wanted to use this project to get more comfortable building a larger project with Next.js & Firebase. I wanted in particular to take advantage of Next.js's API routes and cloud functions to create a REST API for the consumers of content from the CMS. As I developed the REST API I figured it would be a great opportunity to learn GraphQL better and build a GraphQL API. I had used GraphQL a bit getting data from Shopify's APIs for Your Special Sound but this would be the first time I built and configured my own GraphQL server from scratch.

The CMS is organized around the concept of a hub. A hub has several collections and or posts. A collection has one or more fields. There are 4 types of fields: Plain text, numbers, Rich Text, or images/media. I wanted to keep this as simple as possible, keeping in mind that the CMS should be completely oblivious and not care about how or where the content is rendered, therefore there was no need for field types tied to UI or front-end behavior.

The post type is really just a rich text field that has some extra information attached to it like an excerpt, description, author, etc. It mostly exists so that I can create a blog really fast.

When fetched by a consumer all data is output as simple JSON for versatility and compatibility.

creating and integrating the media library

Tightly integrated media library,easy to use & interact with.

The media library is used by the user in 3 ways:

The Media Library Page

Your special Sound

Here the user can upload, delete, edit or search for media that is attached to that current content hub. In the back end, a bucket is created in Google Cloud Storage for each content hub belonging to that user/admin. This would allow me to add folder structures as a feature should i want to, but for now, I went with the WordPress solution with one huge store. One of challenges building this page was to implement search and two different display modes as well as the pagination of media (more on that later).

Therefore the data was separated into a seperate Media context, to separate the data from any components that wanted to interact with it. This made it much easier to have both a list and gallery view as the data remained the same, React simply rendered a different UI component around the same data, which allowed for persistence of things like search state, which image is currently selected and it's metadata etc.

The Media Insert Component

Your special Sound

This component is actually a child component of the Rich Text Editor, and is how the user inserts media quickly inline into the content. Because this component can only render 8 items at a time (to not look ugly and to stay small and compact) I needed to implement pagination for it to work properly. This is another benefit of seperating into a seperate media context, as I created all the pagination logic there and exported them to users of the context

<code goes here>

The Collection Pages

Your special Sound

This one is fairly straight forward. You can simply add an image as a field in a collection. The popup that the user uses to pick the image is much larger than the media insert popup, but also this uses pagination if the number of elements exceeds a certain amount.