How I Built a Simple Blog App with Node.js, Express, EJS & JSON (as a Mini Database)
Nice, let’s turn this into a proper Hashnode article you can copy-paste and tweak.
I’ll write it in Markdown so you can drop it straight into Hashnode’s editor.
# How I Built a Simple Blog App with Node.js, Express, EJS & JSON (as a Mini Database)
I recently built a small full-stack blog application using **Node.js**, **Express**, **EJS**, and **JSON** as a mini database.
In this post, I’ll walk you through what I built, how it works, and what I learned along the way.
> 💡 I used a *little bit* of AI help for UI ideas and styling suggestions, but all the logic, structure, and implementation was done by me.
---
## 🧩 What I Built
The project is a **simple blog platform** where you can:
- ✍️ **Create** a blog post
- 📄 **View** each blog on its own page
- 🗑 **Delete** a blog
- 🌓 Enjoy a **clean dark-themed UI**
I wanted something that feels like a “mini Medium clone” but with very simple tech and no heavy database — so I used a **JSON file** to store all the blog data.
---
## 🛠 Tech Stack
- **Node.js** – JavaScript runtime for the backend
- **Express.js** – web framework for routing and middleware
- **EJS** – template engine for rendering dynamic HTML
- **JSON + fs** – used as a tiny database for storing blog posts
- **CSS** – custom dark theme styling
- **body-parser** – to handle form submissions
---
## 🧱 Project Structure
Here’s a simplified version of the folder layout:
```bash
project/
├── public/
│ └── styles/
│ └── index.css # main styling
│
├── views/
│ ├── index.ejs # home page (list of blogs)
│ ├── blog.ejs # single blog view
│ ├── create-blog.ejs # create form
│ ├── about.ejs # about page
│ └── partials/
│ └── navbar.ejs # navigation bar
│
├── blogs.json # JSON storage for blogs
├── index.js # main Express server
└── package.json
📦 Storing Blogs in JSON
Instead of using a real database like MongoDB or PostgreSQL, I decided to store my posts in a simple JSON file called blogs.json.
A single blog post looks like this:
{
"id": 1712737289120,
"title": "My First Blog",
"content": "This is my first blog post...",
"createdAt": "2025-04-10T12:34:49.120Z"
}
All the posts are stored as an array inside blogs.json.
To safely read the blogs without crashing when the file is empty or missing, I wrote a helper function:
import fs from "fs";
function readBlogs() {
if (!fs.existsSync("blogs.json")) return [];
let data = fs.readFileSync("blogs.json").toString();
if (data.trim() === "") return [];
return JSON.parse(data);
}
This prevents the classic:
SyntaxError: Unexpected end of JSON input
when JSON.parse is used on an empty file.
🌐 Routes Overview
These are the main Express routes I used:
import express from "express";
import bodyParser from "body-parser";
import fs from "fs";
const app = express();
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static("public"));
app.set("view engine", "ejs");
// Home - show all blogs
app.get("/", (req, res) => {
const blogs = readBlogs();
res.render("index.ejs", { blogs });
});
// Create page
app.get("/create", (req, res) => {
res.render("create-blog.ejs");
});
// Submit new blog
app.post("/submit", (req, res) => {
const blogs = readBlogs();
const newBlog = {
id: Date.now(),
title: req.body.title,
content: req.body.content,
createdAt: new Date()
};
blogs.push(newBlog);
fs.writeFileSync("blogs.json", JSON.stringify(blogs, null, 2));
res.redirect("/");
});
// About page
app.get("/about", (req, res) => {
res.render("about.ejs");
});
// Single blog page
app.get("/blog/:id", (req, res) => {
const blogs = readBlogs();
const blog = blogs.find(b => b.id == req.params.id);
if (!blog) {
return res.status(404).send("Blog not found");
}
res.render("blog.ejs", { blog });
});
app.listen(3000, () => {
console.log("Server running on port 3000");
});
Later, it’s also easy to add a delete route using .filter():
app.post("/delete/:id", (req, res) => {
const blogs = readBlogs();
const id = req.params.id;
const updatedBlogs = blogs.filter(blog => blog.id != id);
fs.writeFileSync("blogs.json", JSON.stringify(updatedBlogs, null, 2));
res.redirect("/");
});
🎨 Dark Theme UI
For the styling, I wanted a modern dark look, so I used a dark grey background and lighter cards for contrast:
body {
background-color: #2c2c2c;
color: #f1f1f1;
margin: 0;
padding: 0;
font-family: "Poppins", sans-serif;
}
.home-container {
width: 80%;
max-width: 1100px;
margin: 40px auto;
}
.blogs-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
gap: 25px;
}
.blog-card {
background-color: #353535;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 12px rgba(0,0,0,0.25);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.blog-card:hover {
transform: translateY(-4px);
box-shadow: 0 0 18px rgba(0,0,0,0.35);
}
The individual blog page (blog.ejs) uses a centered card layout with a “Back” button and a readable content area.
🤝 Using a Little Bit of AI
I want to be honest about my process:
I wrote the logic, routes, and file handling myself, and I also structured the project and pages on my own.
I did, however, use a bit of AI assistance for:
refining some CSS (choosing better dark shades & spacing)
getting ideas for button styles and layout adjustments
helping phrase descriptions & README text more clearly
I treated AI like a “pair designer” and “writing assistant”, but I made sure I understood and controlled everything I built.
🧠 What I Learned
How to use EJS to render data from the backend into templates
How to safely read and write JSON files with Node’s
fsmoduleHow to structure a basic CRUD-like blog flow
How to design a consistent dark theme UI
The importance of handling edge cases like empty files
This project was a great hands-on exercise in full-stack JavaScript without relying on a database.
🔗 GitHub Repository
You can find the full source code here:
👉 GitHub Repo: [Add your repo link here]
Example:https://github.com/Nimratjotsingh/blog-app-node-express-json
💬 Final Thoughts
This app is simple, but it gave me a solid understanding of:
sending data from the browser → server → JSON file → back to the browser
rendering dynamic content with EJS
and structuring a small but complete full-stack project.
Next steps I’m considering:
Moving storage from JSON to MongoDB
Adding edit functionality for blogs
Supporting Markdown for blog content
User authentication for personal dashboards
If you have suggestions or feedback, I’d love to hear them! 😊
Thanks for reading!
— Nimratjot Singh