Disclaimer

This was one of the first pro­gram­ming tu­to­ri­als ever writ­ten by me. I would­n’t ad­vise you to read it, the only rea­son I haven’t deleted it is be­cause it’s very nos­tal­gic.


This ar­ti­cle was ini­tially pub­lished on DEV.to, and ever since I wrote that ar­ti­cle a lot of stuff has changed.

I have a lot of blogs. Yep, and the thing is I like all of em. And each one uses a frame­work like Gatsby or a tem­plat­ing en­gine like Nunjucks. But it is cool to have your blog within your web­site. Now, my web­site is built us­ing Express and Express alone [used to be: now my web­site uses Vue]. I don’t use any tem­plat­ing en­gines, it’s just Express routes with HTML files. Having a blog within your web­site (all-in-one-pack) is so awe­some. In this post, I’ll be show­ing you how to cre­ate your own Markdown blog us­ing EJS and Express!

mkdir blog
touch index.ejs
touch blog.ejs

Then, in your main Node.js script file (mine is server.js, most peo­ple have their main script at index.js), we need to con­fig­ure the mark­down parser, EJS and the blog di­rec­tory. For that, we’ll need to in­stall a few pack­ages.

npm i express ejs markdown-it gray-matter body-parser

Once we’ve done that, we need to require all our de­pen­den­cies in server.js (or index.js or what­ever). We’ll also be us­ing the path mod­ule, but we don’t need to in­stall it be­cause it is an in-built mod­ule.

// body parser
const express = require('express');
const app = express();

// if you have a public dir with static scripts and styles
app.use(express.static('public'));

var bodyParser = require("body-parser");
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());

// path for the ejs folder
const path = require("path");

app.set("views", path.join(__dirname, "blog"));
app.set("view engine", "ejs");

// gray-matter to read the .md files better
const matter = require('gray-matter');

Then, we need to de­fine our GET routes for the blog posts.

app.get("/blog/:article", (req, res) => {

// read the markdown file
const file = matter.read(__dirname + '/blog/' + req.params.article + '.md');

// use markdown-it to convert content to HTML
var md = require("markdown-it")();
let content = file.content;
var result = md.render(content);

res.render("index", {
post: result,
title: file.data.title,
description: file.data.description,
image: file.data.image
});
});

What is hap­pen­ing here is that when­ever a user vis­its /blog/article, the fs mod­ule looks in the blog di­rec­tory for a .md file named article. (The URL of the blog post is taken from the file name mi­nus the .md part). And the blog post mark­down file should be lo­cated in the blog di­rec­tory we made at first.

Now, for this to work prop­erly, every blog post mark­down file should be in the fol­low­ing for­mat:

---
title: Title Here
description: A nice description of this post
image: A nice image representing the blog post, mainly meant for the <meta> tags
---

Blog post content here
...

The for­mat is the rea­son why I used the gray-matter pack­age. And markdown-it con­verts the Markdown stuff to HTML.

Also, we need a GET route to show all our blog posts (blog.ejs).

app.get("/blog", (req, res) => {
const posts = fs.readdirSync(__dirname + '/blog').filter(file => file.endsWith('.md'));
res.render("blog", {
posts: posts
});
});

And fi­nally, for our EJS files.
index.ejs

<html>
<body>
<div id="blog">
<h1><%= title %></h1>
<p><%= description %></p>
<%- post %>
</div>
</body>
</html>

NOTE: An EJS vari­able named image (<%= image %>) is also avail­able which is the im­age url men­tioned in the blog post mark­down file header.

blog.ejs

<html>
<body>
<div id="blog">
<% for (post of posts) { %>
<a href="/blog/<%= post.slice(0, -3) %>">
<div class="post">
<%= post.toUpperCase() %>
</div>
</a>
<% } %>
</div>
</body>
</html>

And that’s it!

Of course, this is just a ba­sic tem­plate for you to use, you can al­ways add more HTML and style us­ing CSS to cus­tomize ac­cord­ing to your needs!