LinkedIn
Email
July 2, 2022 4 min
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.
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.
Let's dive in with the npm packages you'll need for this app.
npm
markdown-it
E.g., ### h3 sub-heading → <h3>h3 sub-heading</h3>
### h3 sub-heading
<h3>h3 sub-heading</h3>
gray-matter
readFileSync
--- 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...' } }
Let's first require all the necessary packages and set our view engine to ejs:
view engine
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.
index.js
/blog
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.
GET
/blog/:article
fs
.md
article
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.