Open In App

Personal Blogging Platform: Mastering Database Architecture and CRUD Operations

Last Updated : 23 Jul, 2025
Comments
Improve
Suggest changes
Like Article
Like
Report

A personal blogging platform is an excellent project to explore the fundamentals of database design and management, along with its integration into scalable web applications. It provides the foundational infrastructure to handle blog-related operations such as creating, reading, updating, and deleting articles.

In this project ensures scalability, maintainability, and efficiency by implementing a robust database and using the MVC (Model-View-Controller) architecture. This article covers the project’s structure, features, database design, and complete implementation code for creating a personal blogging platform.

Blogging Platform Features

We have implemented the following features in this project.

CRUD Operations on Articles:

  • Create: Add new blog posts.
  • Read: Fetch a list of articles or a specific article by ID.
  • Update: Modify existing articles.
  • Delete: Remove articles by ID.

Filter Articles: Filter by tags or publishing date.

User Management: Manage authors for articles.

Additional Features:

  • Commenting system.
  • Like system for articles.

Architecture of Blogging Platform

The architecture of the Blogging Platform API is based on the MVC (Model-View-Controller) design pattern, ensuring clear separation of concerns and scalability.

Architecture_Blogging_Platform
Architecture Blogging Platform

Here’s a breakdown of the architecture:

Client Layer

  • Represents the users interacting with the system via tools like Postman, web browsers, or frontend applications.
  • Sends HTTP requests to the backend server using RESTful methods (GET, POST, PUT, DELETE).

Server Layer

  • Routes: Define API endpoints that map user requests to specific controller functions.
  • Controllers: Handle the business logic, process user requests, and invoke models for database operations.
  • Middleware: Handles JSON parsing, error handling, and other pre-processing tasks for incoming requests.
  • Models: Act as the interface to the database, executing SQL queries for CRUD operations.

Database Layer

  • Relational Database: Stores persistent data in normalized tables (users, articles, comments, likes).
  • Relationships:
    • users → articles (One-to-Many): A user can create multiple articles.
    • articles → comments (One-to-Many): An article can have multiple comments.
    • articles → likes (One-to-Many): An article can have multiple likes.

Data Flow

  • The client sends a request to a route.
  • The route invokes the corresponding controller function.
  • The controller processes the request and interacts with the model to fetch or manipulate data in the database.
  • The database returns results to the model, which then passes them to the controller.
  • The controller sends the final response back to the client.
  • This architecture ensures modularity, maintainability, and scalability, making it suitable for real-world applications.

Database Design - Blogging Platform

CREATE DATABASE personal_blog;

USE personal_blog;

-- Users Table
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
email VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP
);

-- Articles Table
CREATE TABLE articles (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
title VARCHAR(255) NOT NULL,
content TEXT NOT NULL,
tags VARCHAR(255),
published_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

-- Comments Table
CREATE TABLE comments (
id INT AUTO_INCREMENT PRIMARY KEY,
article_id INT NOT NULL,
user_id INT NOT NULL,
content TEXT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

-- Likes Table
CREATE TABLE likes (
id INT AUTO_INCREMENT PRIMARY KEY,
article_id INT NOT NULL,
user_id INT NOT NULL,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (article_id) REFERENCES articles(id) ON DELETE CASCADE,
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);

Output:

Database_Design_Blogging_Platform
Database Design - Blogging Platform

Project Structure - Blogging Platform

The project follows the MVC (Model-View-Controller) architecture, which separates the application logic into distinct layers for better organization and maintainability.

  • controllers/: Contains the logic for handling requests and responses.
  • models/: Defines how data is structured and includes methods to interact with the database.
  • routes/: Manages the API endpoints and connects them to corresponding controllers.
  • config/: Handles database connection settings.
  • app.js: The main entry point of the application.

Blogging Platform - Code Implementation

Here we will explain all the codeand structures of the projects in detail in step wise.

1. Database Configuration

This step establishes the connection between the application and the MySQL database. The configuration ensures that all database queries are routed through a pool of connections, optimizing performance and handling multiple requests efficiently. By creating a dbConfig.js file, we centralize the database settings, making it easier to manage and update.

The configuration integrates with the project by exporting a promise-based connection pool, which is used by the models (like articleModel.js) to execute SQL queries. This approach keeps the database logic separate from the rest of the application, adhering to the MVC architecture and enhancing maintainability.

config/dbConfig.js

config/dbConfig.js
// Importing required modules for database connection
const mysql = require('mysql2');
require('dotenv').config(); // Load environment variables from .env file

// Creating a connection pool for efficient database querying
const pool = mysql.createPool({
  host: process.env.DB_HOST, // Database host
  user: process.env.DB_USER, // Database user
  password: process.env.DB_PASSWORD, // Database password
  database: process.env.DB_NAME, // Database name
});

// Exporting a promise-based pool to allow async/await syntax in queries
module.exports = pool.promise();

2. Article Model

The Article Model (models/articleModel.js) serves as the intermediary between the database and the application logic. It contains methods that correspond directly to specific CRUD operations required by the API endpoints:

  • getAllArticles(filters): Fetches all articles from the database. It supports filtering by tags or publishing date using query parameters.
  • getArticleById(id): Retrieves a single article by its ID. This method is used when a user requests detailed information about a specific article.
  • createArticle(data): Inserts a new article into the database. The data parameter includes the article’s title, content, and tags.
  • updateArticle(id, data): Updates an existing article in the database. The id identifies the article to be updated, and data contains the new values for the title, content, or tags.
  • deleteArticle(id): Deletes an article from the database based on its ID.

Each method integrates with the database configuration (dbConfig.js) to execute SQL queries. These methods ensure that the model handles all interactions with the articles table, encapsulating database logic and maintaining separation of concerns within the MVC architecture.

models/articleModel.js

models/articleModel.js
// Article Model: Handles all interactions with the 'articles' table
class Article {
  // Retrieve all articles with optional filters for tag and date
  static async getAllArticles(filters = {}) {
    const { tag, date } = filters;
    let query = 'SELECT * FROM articles WHERE 1=1'; // Base query
    const params = [];

    if (tag) {
      query += ' AND FIND_IN_SET(?, tags)'; // Add condition for tag filter
      params.push(tag);
    }

    if (date) {
      query += ' AND DATE(published_at) = ?'; // Add condition for date filter
      params.push(date);
    }

    // Execute the query and return the result
    const [rows] = await db.execute(query, params);
    return rows;
  }

  // Fetch a specific article by its ID
  static async getArticleById(id) {
    const [rows] = await db.execute('SELECT * FROM articles WHERE id = ?', [id]);
    return rows[0]; // Return the first row, or undefined if not found
  }

  // Insert a new article into the database
  static async createArticle(data) {
    const { title, content, tags } = data;
    const [result] = await db.execute(
      'INSERT INTO articles (title, content, tags) VALUES (?, ?, ?)',
      [title, content, tags]
    );
    return result.insertId; // Return the ID of the newly created article
  }

  // Update an existing article by its ID
  static async updateArticle(id, data) {
    const { title, content, tags } = data;
    await db.execute(
      'UPDATE articles SET title = ?, content = ?, tags = ? WHERE id = ?',
      [title, content, tags, id]
    );
  }

  // Delete an article by its ID
  static async deleteArticle(id) {
    await db.execute('DELETE FROM articles WHERE id = ?', [id]);
  }
}

module.exports = Article;

3. Article Controller

The Article Controller (controllers/articleController.js) handles the logic for processing incoming requests related to articles. It uses methods from the Article Model to interact with the database and sends appropriate responses back to the client:

  • getAllArticles(req, res): Retrieves all articles from the database. It extracts query parameters from req.query to filter articles by tags or publishing date. The response contains the filtered list of articles or all articles if no filters are applied.
  • getArticleById(req, res): Fetches a specific article by its ID from req.params.id. If the article does not exist, it responds with a 404 error.
  • createArticle(req, res): Adds a new article to the database using data from req.body. On success, it responds with the ID of the newly created article and a success message.
  • updateArticle(req, res): Updates an existing article based on its ID (req.params.id) and new data (req.body). It responds with a success message after the update.
  • deleteArticle(req, res): Deletes an article by its ID (req.params.id). It responds with a success message after the deletion.

These methods ensure the controller acts as a bridge between the client requests and database operations, maintaining the separation of concerns in the MVC architecture.

controllers/articleController.js

controllers/articleController.js
// Importing the Article model for database operations
const Article = require('../models/articleModel');

// Controller to handle fetching all articles
exports.getAllArticles = async (req, res) => {
  try {
    const filters = req.query; // Extract filters from the query string
    const articles = await Article.getAllArticles(filters);
    res.json(articles); // Respond with the list of articles
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch articles' });
  }
};

// Controller to fetch a single article by ID
exports.getArticleById = async (req, res) => {
  try {
    const article = await Article.getArticleById(req.params.id);
    if (!article) return res.status(404).json({ error: 'Article not found' });
    res.json(article);
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch article' });
  }
};

// Controller to create a new article
exports.createArticle = async (req, res) => {
  try {
    const id = await Article.createArticle(req.body); // Create article in database
    res.status(201).json({ message: 'Article created', id });
  } catch (error) {
    res.status(500).json({ error: 'Failed to create article' });
  }
};

// Controller to update an existing article by ID
exports.updateArticle = async (req, res) => {
  try {
    await Article.updateArticle(req.params.id, req.body); // Update the article
    res.json({ message: 'Article updated' });
  } catch (error) {
    res.status(500).json({ error: 'Failed to update article' });
  }
};

// Controller to delete an article by ID
exports.deleteArticle = async (req, res) => {
  try {
    await Article.deleteArticle(req.params.id); // Delete the article
    res.json({ message: 'Article deleted' });
  } catch (error) {
    res.status(500).json({ error: 'Failed to delete article' });
  }
};

4. Routes

The Routes (routes/articleRoutes.js) define the API endpoints and link them to the appropriate controller methods. This modular approach simplifies route management and ensures clear separation of concerns:

  • GET /: Calls articleController.getAllArticles to retrieve all articles, optionally filtered by query parameters (e.g., tags, publishing date).
  • GET /:id: Calls articleController.getArticleById to fetch a specific article by its ID.
  • POST /: Calls articleController.createArticle to add a new article. The article details are passed in the request body.
  • PUT /:id: Calls articleController.updateArticle to modify an existing article by its ID. Updated data is passed in the request body.
  • DELETE /:id: Calls articleController.deleteArticle to remove an article by its ID.

The Routes module centralizes endpoint definitions, linking them to the corresponding controller methods for a clean and scalable API structure.

routes/articleRoutes.js

routes/articleRoutes.js
// Importing the required modules
const express = require('express');
const articleController = require('../controllers/articleController');

const router = express.Router(); // Create a router instance

// Define the routes and map them to controller methods
router.get('/', articleController.getAllArticles); // Fetch all articles
router.get('/:id', articleController.getArticleById); // Fetch a single article by ID
router.post('/', articleController.createArticle); // Create a new article
router.put('/:id', articleController.updateArticle); // Update an article by ID
router.delete('/:id', articleController.deleteArticle); // Delete an article by ID

module.exports = router; // Export the router for use in app.js

5. Application Entry Point

This step involves setting up the entry point of the application, which ties together all the components of the project. The app.js file serves as the backbone of the API, handling middleware, routing, and server initialization.

This setup is crucial for creating a functional and scalable application that can handle requests and responses seamlessly.

app.js

app.js
const express = require('express');
const bodyParser = require('body-parser');
const articleRoutes = require('./routes/articleRoutes'); // Import article routes

const app = express();

// Use body-parser middleware to parse JSON requests
app.use(bodyParser.json());

// Mount the article routes under the /api/articles path
app.use('/api/articles', articleRoutes);

// Start the server and listen on the specified port
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));

Conclusion

This project, the Personal Blogging Platform API, serves as an excellent example of how to design and implement a RESTful API using Node.js and MySQL. By following the MVC (Model-View-Controller) architecture, it ensures clear separation of concerns, modularity, and scalability. The project introduces real-world database concepts like relationships, foreign keys, and SQL operations, making it a great hands-on exercise for anyone looking to strengthen their understanding of backend development and database integration.


Article Tags :

Similar Reads