Julian Terenzio Profile Pic
Julian Terenzio

Building A Markdown Blog App with Express and EJS

A simple stack to develop and launch your first blog application.

F1

Photo by Ferenc Almasi on Unsplash

The blog you're reading right now is the end-product of what you'll be able to build after finishing this tutorial. Meta right? The blog app will be built with a Node.js (Express) backend and the frontend will be templated with EJS into raw HTML and CSS. I understand templating with EJS is basically fossil record nowadays with frameworks like React dominating web development, but this seems to be the fastest way to ship a blog app if you're already familiar with building HTML/CSS/JS frontends.

Why I Used Markdown Files instead of a Mongo Database

What about a database? No need. This app will allow you to write blog content within a Markdown (i.e., text) file, and the backend logic will convert each file into it's own templated webpage. Using a database like Mongo is the go-to way to store data for most developers, but markdown files allow for better version control and performance which is especially needed for writing blogs. Since all my blog posts are saved in local markdown files, I can update and push changes to Heroku without querying and updating an external database. Fetching from an external database will also add unnecessary complexity if data is fetched through a poor internet connection. Let's keep it simple.

Necessary NPM Packages

Let's dive in with the npm packages you'll need for this app.

E.g., ### h3 sub-heading<h3>h3 sub-heading</h3>

---
title: Finding The Limit
subtitle: Lights out and away we go...
---
### For fans old and new...
{
   content: '<h3>For fans old and new...</h3>',
   data: { 
      title: 'Finding The Limit', 
      subtitle: 'Lights out and away we go...' 
   }
}

Backend Logic and EJS Templating

Let's first require all the necessary packages and set our view engine to ejs:

const express = require('express');
const app = express();
const path = require('path');
const bodyParser = require("body-parser");
const fs = require('fs');
// gray-matter to read the .md files better
const matter = require('gray-matter');

/// MIDDLEWARE

app.set('views', path.join(__dirname, 'blog'));
app.set('view engine', 'ejs');
// allows the server to served in any directory
app.use(express.static(path.join(__dirname, 'public')));
// For parsing form data (key:value) pairs
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

I then created a function in my index.js file to find and sort every markdown blog within the local blog subdirectory in order to convert the content into a list of blog objects. This is the data structure that will be used to display all the information needed for the /blog URL subdirectory.

function getBlog() {
    const blogFiles = fs.readdirSync(__dirname + '/blog').filter(blogFile => blogFile.endsWith('.md'));
    const blogContent = [];
    for (blog of blogFiles) {
        blogContent.push(matter.read(__dirname + '/blog/' + blog));
    }
    const blogs = blogFiles.map(function (blog, i) {
        return [blog, blogContent[i]];
    });
    // Displays articles based on pre-defined order
    blogs.sort(function (a, b) {
        return a[1].data.order - b[1].data.order;
    });
    return blogs;
}

app.get("/blog", (req, res) => {
    // read the markdown file for main blog page
    res.render("blog-home.ejs", {
        blogs: getBlog()
    });
});

The next step is to create a GET route for each blog in the subdirectory. Every time a user visits the URL, /blog/:article, the fs module will take a peak into the blog directory for any .md file named article. This will generate the correct file name since the URL of the blog post is taken directly from the blog subdirectory.

app.get("/blog/:article", (req, res) => {
    // read the markdown file
    const article = req.params.article;
    const blogFile = matter.read(__dirname + '/blog/' + article + '.md');

    const md = require('markdown-it')()
    const content = blogFile.content;
    const result = md.render(content);

    res.render("blog.ejs", {
        title: blogFile.data.title,
        subtitle: blogFile.data.subtitle,
        topic: blogFile.data.topic,
        description: blogFile.data.description,
        time: blogFile.data.time,
        date: blogFile.data.date,
        image: blogFile.data.image,
        imageSubtitle: blogFile.data.imageSubtitle,
        post: result
    });
});

The GET request renders all the necessary datapoints in object format to the frontend so EJS can easily display the content. The content data is templated like this:

<div class="blog-page">
    <button class="topic-tag text-small">
        <%= topic %>
    </button>
    <h4 class="blog-title">
        <%= title %>
    </h4>
    <h4 class="blog-subtitle">
        <%= subtitle %>
    </h4>
</div>

<div class="thumbnail">
    <img src="<%= image %>" alt="F1" class="blog-banner-image">
    <p class="thumbnail-caption">
        <%= imageSubtitle %>
    </p>
</div>
<div class="text-section">
    <%- post %>
</div>

Voilà! You just created your first blog application with less than 100 lines of backend code.

Thanks for reading :) You can learn more about me here.