Skip to content
Santekno.com | Tech Tutorials and Trends
EN
📖 0%
29 Sep 2025 · 6 min read ·Article 91 / 125
Go

91 Case Study: Article Management System (CMS)

IH
Ihsan Arif
Writer at Santekno · Backend Engineer

91 Case Study: Article Management System (CMS)

A CMS (Content Management System) is one of the most fundamental systems in the modern software ecosystem. In today’s fully digital era, the need to manage content in a structured, scalable, and secure way is critical—whether for a personal blog, a news portal, or an enterprise knowledge base.

This article is part of the 91 Case Studies series, in which we dissect the architecture, implementation, and best practices of building a CMS using an engineering-first approach, while also presenting a case study and pragmatic implementation code examples.


1. Problem & Requirements

Case Study: The “MediaKita” News Portal

Scenario:
MediaKita is a growing news portal. Initially, articles were uploaded manually by the team via Google Docs, then copy-pasted into the website by developers. This process was not scalable and frequently caused problems, such as:

  • Formatting and metadata errors.
  • Duplicate articles.
  • Frequently missed deadlines.
  • No approval/editorial workflow system.

Key Requirements from stakeholders:

  • Every user (author) can write, save, and publish articles.
  • Articles must be categorizable, taggable, and have a status (draft, review, published).
  • Editors can review and approve articles before they are published.
  • The system must be trackable (audit trail).
  • A REST API for frontend & mobile app integration.

2. High Level Design

Let’s break this down into several components:

MERMAID
flowchart TD
    subgraph Frontend
        FE[React/Vue SPA]
    end
    subgraph Backend
        BE[REST API (Express/NestJS)]
    end
    subgraph Database
        DB[(PostgreSQL/MongoDB)]
    end
    FE -- HTTP/JSON --> BE
    BE -- ORM/ODM --> DB

Entity Model

The following table illustrates the 3 core entities in a simple CMS system.

EntityFieldsRelationship
Userid, name, email, role (author/editor/admin), password_hash1 User : n Article
Articleid, title, slug, content, category_id, status, created_at, updated_at, author_id, editor_id, audit_logn Article : 1 Category
Categoryid, name, slug, parent_idn Category : n Article

User Stories

1. Author Writes an Article

  • The user opens the form page, fills in the title and content, and selects a category.
  • Saves it as a draft.

2. Editorial Workflow

  • The draft enters the review queue.
  • The editor provides feedback or approves it.
  • Once approved, the article status changes to published.

3. Audit Trail

  • Every status or content change is recorded in the audit log.

3. Code Example: Backend API (Node.js + Express + Sequelize)

Let’s see how the context above translates into RESTful endpoints:

javascript
 1// src/routes/articles.js
 2const express = require('express');
 3const router = express.Router();
 4const { Article, Category, User } = require('../models');
 5const auth = require('../middleware/auth');
 6
 7// Endpoint: Create an article (Draft)
 8router.post('/', auth, async (req, res) => {
 9  try {
10    const { title, content, category_id } = req.body;
11    const article = await Article.create({
12      title, content, category_id,
13      author_id: req.user.id,
14      status: 'draft'
15    });
16    res.status(201).json(article);
17  } catch (error) {
18    res.status(400).json({ error: error.message });
19  }
20});
21
22// Endpoint: Update article status to 'review'
23router.post('/:id/submit', auth, async (req, res) => {
24  try {
25    const article = await Article.findByPk(req.params.id);
26    if (article.author_id !== req.user.id) return res.status(403).json({ error: "Unauthorized" });
27    article.status = 'review';
28    await article.save();
29    res.json(article);
30  } catch (error) {
31    res.status(400).json({ error: error.message });
32  }
33});
34
35// Endpoint: Approve an article (Editor)
36router.post('/:id/approve', auth, async (req, res) => {
37  try {
38    if (req.user.role !== 'editor') return res.status(403).json({ error: "Editor only" });
39    const article = await Article.findByPk(req.params.id);
40    article.status = 'published';
41    article.editor_id = req.user.id;
42    await article.save();
43    res.json(article);
44  } catch (error) {
45    res.status(400).json({ error: error.message });
46  }
47});

4. Editorial Workflow: State Machine

To keep the editorial process robust, the status workflow is usually managed via a state machine.

MERMAID
stateDiagram-v2
    [*] --> Draft
    Draft --> Review: Submit for Review
    Review --> Draft: Request Change
    Review --> Published: Approve
    Published --> Archived: Archive
    Draft --> Deleted: Delete
    Review --> Deleted: Delete

5. Audit Trail: Simulation & Implementation

An audit trail is important for governance and debugging. Here’s a simple simulation using a hook on the ORM model:

javascript
1// src/models/Article.js (Sequelize Model)
2Article.afterUpdate(async (article, options) => {
3  await AuditLog.create({
4    article_id: article.id,
5    changed_by: options.user.id,
6    status: article.status,
7    timestamp: new Date()
8  });
9});

Every time an article’s status changes, a record is inserted into the audit_logs table.

Audit Log Query Example

sql
1SELECT * FROM audit_logs WHERE article_id = 123 ORDER BY timestamp DESC;

6. Categories and Tagging

Organizing content is essential for navigation and discoverability. For example, a single article can have many tags.

Many-to-Many Relationship Table:

article_idtag_id
103
108
123

7. Frontend Integration

With a REST API in place, the frontend can be built with modern frameworks such as React.

Example Article Fetch Simulation:

jsx
1useEffect(() => {
2  fetch("/api/articles?status=published")
3    .then(res => res.json())
4    .then(data => setArticles(data));
5}, []);

8. Deployment & Scalability

  • Database: PostgreSQL, with full-text search as an option for article search.
  • Backend: Node.js/NestJS/Go, with stateless services recommended for easier scaling.
  • Frontend: SPA, caching important data on a CDN when traffic is high.
  • OAuth Integration: For secure login.

9. Live Case Study: Workflow Simulation

Editorial Flow:

  1. The author creates a draft article.
  2. Submits it for review.
  3. The editor provides feedback (request change) or approves it.
  4. If approved, the article is published automatically.

API Request Simulation:

bash
1# 1. Author create
2curl -X POST /api/articles -d '{ "title": "CMS Rules", "content": "...", ... }'
3
4# 2. Author submit
5curl -X POST /api/articles/5/submit
6
7# 3. Editor approve
8curl -X POST /api/articles/5/approve

Sample Audit Trail Table:

IDArticle IDChanged ByStatusTime
1512draft2024-06-01 09:01:01
2512review2024-06-01 09:02:00
3513published2024-06-01 11:10:00

10. Lessons Learned & Best Practices

  • Separation of Concerns: Strictly separate the model, controller, and service layers.
  • Access Control: Implement RBAC (Role-Based Access Control) on both endpoints and the UI.
  • State Machine: Don’t hardcode statuses as magic strings; use the state machine pattern instead.
  • Audit Trail: With a change log, we can easily trace who changed what.
  • API-First: Design the backend so it can be easily integrated with frontend/web/mobile.
  • Validation & Error Handling: Don’t forget the validation layer on both the backend and frontend.

11. Conclusion

Building a CMS is not just about CRUD operations on articles—it’s about designing a workflow system, ensuring data integrity, and enabling easy, enterprise-ready scaling. From the MediaKita case study, editorial needs that once relied on manual processes can now be automated and properly monitored. Its main value lies in flexibility, governance, and extensibility.

We hope this case study breakdown helps those of you who are designing or refactoring a CMS to scale up to the next level. Don’t hesitate to experiment, iterate, and always audit every critical flow in your system!

What has your experience been like building a CMS? Share it in the comments!

Related Articles

💬 Comments