Flexible Blog System (Create, Edit, Publish, View)

So the whole idea behind this website being to show things I can create, I felt like it was appropriate to build my blog system from scratch, from the UI to create, edit, save, and publish to the blogs themselves. In fact, I'm using that system right now to write this. So thankfully that means my plan worked.

Philosophy

In my efforts to always have free tools for my personal hobby projects, that unfortunately means I don't have a huge amount of leeway in terms of database storage space - certainly not enough for the millions of users I'm expecting to use this blog system. Therefore, my philosophy moving forward will be to have database storage reserved exclusively for admin users (who can login), whereas others will also be able to use the tools that I create, but without permanence. For instance, an admin user will be able to publish a blog that has its own dedicated url, but visitors will only be able to get temporary blogs that erase once the session terminates.

Database Schema

A good project always starts with a fantastic database schema. I decided that you could either save or publish a project: saved projects would not appear for others, just for the user that saved them, and published projects would have their own dedicated url and be shareable. In both cases, the user could edit them after being saved or published. I went through a couple iterations of ideas, even thinking about having historic version rollback, but I felt like that would be beyond my own needs. However, to include as much scalability as possible, I still incorporated the version idea with one to one mapping with my blogs. My prisma/schema.prisma now looked like this:

model User {
  id        String   @id @default(auto()) @map("_id") @db.ObjectId
  ... other not relevant user fields ...
  Blog      Blog[]
}

model Version {
  id        String   @id @default(auto()) @map("_id") @db.ObjectId
  createdAt DateTime @default(now())
  contentMD String
  blog      Blog?
}

model Blog {
  id        String   @id @default(auto()) @map("_id") @db.ObjectId
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  published Boolean  @default(false)
  current   Version  @relation(fields: [versionId], references: [id])
  versionId String   @unique @db.ObjectId
  user      User     @relation(fields: [userId], references: [id])
  userId    String   @db.ObjectId
  username  String
}

So Blogs governed overall blog data, linking it to a version and a user as well as published state, while Versions contained the actual content - this could be expanded with dedicated title, description, tags fields down the line.

Markdown

For some reason, I really like Markdown. Maybe it's just because of the Github PR interface or because of Notion with its adjacent syntax, but it just feels clean and readable and quick, most importantly - I wanted to be able to create visually interesting documents quickly for anyone to read. Looking for some packages to build a Markdown editor, some were out of the box ones with dedicated toolbars, extended features, great UI... I feel like I should say I didn't implement them because of reducing overall package size, being independent, having more customizability, but I just couldn't figure out how to add them because of ESM/CJS incompatibility. But honestly, I feel like it turned out pretty well! I feel like I have a lot more room to change things around, and this extremely minimal system has just enough features for me.

Images

Going back to my earlier cost philosophy, I didn't exactly want to rely on AWS free credits for an S3 instance just to house a few images from my blogs. I decided to opt for the ImgBB service because they seem unlimited and free? I don't entirely know but they have a solid API and it's been working for me so far! alt text

Execution

The rest of this project was pretty straightforward: database linking and overall control flow on the backend was pretty simple, and UI design took up the most part since I'm not as familiar and I didn't want to take any shortcuts on this project to try to make it as clean as possible. The temporary blogs complicated things a little bit and gave me a little trouble in reconciling Remix Run's Action and Loader data and React's state systems, but managing to find a resolution in both was fantastic and resulted in a pretty cool blog system!