How to build a blog with NodeJS
Just want the code? Visit the repo
If you're looking to start a blog (or if you're thinking of redesigning yours although you haven't posted in 2 years), you'll stumble upon a lot of options and it can be incredibly daunting; and if you stumble with the newest Josh's post about his stack it is easy to feel overwhelmed with the shown stack.
But you shouldn't feel like that and starting small is key to being sustainable
And how do I know that? Because I feel that sense of feeling overwhelmed as well!
At this date, this website is done with NextJS, Contentful, and Markdown and while adding posts to it is not particularly hard, maintaining it is!
I haven't added anything code-related to this website since 2021 and at this point I don't even know if I'm able to run it locally (and I'm reticent even to try it out)!
For this ? particular reason, I want to preach for a simple stack; something that endures the test of time; something that 'just works'; so let's jump right into it, shall we?
Start that project!
Keep in mind that this project will be very, very barebones but it should give you a good foundation for you to develop on top of it and reach for the sky.
We'll start by initializing a Node project inside a chosen folder (nodejs-blog for me) with and installing a couple of dependencies that I feel like will make our lives easier, like Express, EJS, Marked, the good ol' body-parser and gray-matter.
npm init npm install body-parser ejs express marked gray-matter
Explaining the dependencies
The reason why I chose to add EJS into the mix was to make things a bit easier for me, by taking advantage of templates and just writing less code overall. If you're not familiar with it, just wait. It's pretty cool!
For Marked and gray-matter, it's pretty simple: markdown rules and I want my posts to have proper metadata, which I plan to create with frontmatter.
Alright, back at it!
Now open your project in your favourite IDE and create your main.js file. I know that we'll want the following routes: /, /:post, and that we'll need to have relevant stuff on the public folder, so our initial main.js can look like this:
// main.js const express = require("express"); const fs = require("fs"); const path = require("path"); const { marked } = require("marked"); const matter = require("gray-matter"); const app = express(); const PORT = process.env.PORT || 3000; app.use(express.static("public")); app.set("view engine", "ejs"); app.get("/", (req, res) => {}); app.get("/:post", (req, res) => {}); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
Pretty straightforward, right? The idea is to have the list of posts on my home (or /) and just have individual pages for my posts.
Time to template like you never templated before!
With the base setup out of the way, we also need a base structure and EJS will provide that.
Start by creating a folder named views; this will be the root of your pages, so to speak, which means that you can create a home.ejs and a post.ejs inside it just to mark the two types of pages that we'll have.
Create also a folder, inside views, named partials; you can think of it as our components and you can already create 3 files here: header.ejs, footer.ejs and head.ejs.
This is the base structure of our blog: 2 pages and 3 components, that's it. All the rest will be dealt with inside main.js
The partials
Like I've mentioned, templates allow us to not have to repeat as much code as we would have to if we were creating each page by hand, and our setup provides us exactly with a ease of mind regarding that.
npm init npm install body-parser ejs express marked gray-matter
// main.js const express = require("express"); const fs = require("fs"); const path = require("path"); const { marked } = require("marked"); const matter = require("gray-matter"); const app = express(); const PORT = process.env.PORT || 3000; app.use(express.static("public")); app.set("view engine", "ejs"); app.get("/", (req, res) => {}); app.get("/:post", (req, res) => {}); app.listen(PORT, () => { console.log(`Server is running on http://localhost:${PORT}`); });
// head.ejs <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta http-equiv="X-UA-Compatible" content="IE=edge" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title>Here's my blog</title> </head> </html>
Basically, the regular head at, well, head, the closing tags at footer and the navbar, and opening tags at header. Pretty simple, right?
The views
Now that we have our components we can get the pages going.
// footer.ejs </main> </body> </html>
// header.ejs <body> <main> <header> <a href="/">Blog</a> </header>
Yeah, it looks pretty weird but just know that the include brings the partials into our views and that there's extra syntax to make it work (go to the docs if you're interested in how it works).
The <%- allows us to not double-escape our HTML ( try it out with <% or <%= at the end and see what happens) and the forEach(), well, does exactly what a forEach does. Nothing particularly new here, just a different way of writing stuff that you already know!
But, rejoice, you've now interacted with a new tool! ?
The blog posts! ?
At the root of your project create a posts folder and your first blog-post-1.md inside of it with the following content:
// home.ejs <%- include('./partials/head') %> <%- include('./partials/header') %> <div> <h2>The posts:</h2> <ul> <% posts.forEach((post)=> { %> <li> <a href="<%= post.link %>"><%= post.title %></a> </li> <% }) %> </ul> </div> <%- include('./partials/footer') %> </p> <p>What's inside the --- is our frontmatter, and you'll get to use it right away!</p> <h2> Time to see some stuff on the screen! </h2> <p>Back to our main.js, we'll first deal with the / route. As we've seen, we want to be able to get our posts and loop over them to display info about them on the screen.</p> <p>To simplify stuff I'll leave comments next to each relevant line instead of writing huge blocks of text explaining stuff! ?<br> </p> <pre class="brush:php;toolbar:false">// post.ejs <%- include('./partials/head') %> <%- include('./partials/header') %> <h1><%= frontmatter.title %></h1> <p><%= frontmatter.date %></p> <p><%= frontmatter.author %></p> <%- content %> <%- include('./partials/footer') %>
Now run node main.js in your terminal and visit localhost:3000. You should see your / route populated with links to the markdown files that you created! ?
There's a lot to digest there so, please, try every code line by yourself and see if it makes sense. Try to do different stuff, actually! Get the summary for your posts and find a way of displaying it inside the home.ejs file. Go crazy with it! Attach image urls and also try to display them. PLAY WITH IT!
Now, for the /post itself:
--- title: "Blog post 1" date: 2024-10-31 author: "Rui Sousa" summary: "Here's the first blog post" --- # A blog post Here's my first blog post!
Once again, run node main.js, and choose one of the links in the homepage. You should see your markdown file rendered as HTML!
As before, try stuff out; add elements to the markdown and see how they render; add new fields to the frontmatter and also get them to show.
You're now the proud owner of a blog made with Node! ?
That's it
There's a lot more that we could do here but that's out of the scope, isn't it? We got something working, with what we intended to do, and that is perfect. Now it's your turn to ✨ make it shine ✨
See if you can change the head.ejs info by passing properties to it! Ideally, the tab name would change with the chosen content. And we should also have proper metadata when we share the website on social media so we also need that frontmatter info inside the head. Sounds like a good challenge, uh? ?
As always, if you have any doubts, feel free to reach me via X.
The above is the detailed content of How to build a blog with NodeJS. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undress AI Tool
Undress images for free

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

TypeScript's advanced condition types implement logical judgment between types through TextendsU?X:Y syntax. Its core capabilities are reflected in the distributed condition types, infer type inference and the construction of complex type tools. 1. The conditional type is distributed in the bare type parameters and can automatically split the joint type, such as ToArray to obtain string[]|number[]. 2. Use distribution to build filtering and extraction tools: Exclude excludes types through TextendsU?never:T, Extract extracts commonalities through TextendsU?T:Never, and NonNullable filters null/undefined. 3

Microfrontendssolvescalingchallengesinlargeteamsbyenablingindependentdevelopmentanddeployment.1)Chooseanintegrationstrategy:useModuleFederationinWebpack5forruntimeloadingandtrueindependence,build-timeintegrationforsimplesetups,oriframes/webcomponents

varisfunction-scoped,canbereassigned,hoistedwithundefined,andattachedtotheglobalwindowobject;2.letandconstareblock-scoped,withletallowingreassignmentandconstnotallowingit,thoughconstobjectscanhavemutableproperties;3.letandconstarehoistedbutnotinitial

Optionalchaining(?.)inJavaScriptsafelyaccessesnestedpropertiesbyreturningundefinedifanypartofthechainisnullorundefined,preventingruntimeerrors.1.Itallowssafeaccesstodeeplynestedobjectproperties,suchasuser.profile?.settings?.theme.2.Itenablescallingme

This article explores in-depth how to automatically generate solveable puzzles for the Double-Choco puzzle game. We will introduce an efficient data structure - a cell object based on a 2D grid that contains boundary information, color, and state. On this basis, we will elaborate on a recursive block recognition algorithm (similar to depth-first search) and how to integrate it into the iterative puzzle generation process to ensure that the generated puzzles meet the rules of the game and are solveable. The article will provide sample code and discuss key considerations and optimization strategies in the generation process.

The most common and recommended method for removing CSS classes from DOM elements using JavaScript is through the remove() method of the classList property. 1. Use element.classList.remove('className') to safely delete a single or multiple classes, and no error will be reported even if the class does not exist; 2. The alternative method is to directly operate the className property and remove the class by string replacement, but it is easy to cause problems due to inaccurate regular matching or improper space processing, so it is not recommended; 3. You can first judge whether the class exists and then delete it through element.classList.contains(), but it is usually not necessary; 4.classList

JavaScript's class syntax is syntactic sugar inherited by prototypes. 1. The class defined by class is essentially a function and methods are added to the prototype; 2. The instances look up methods through the prototype chain; 3. The static method belongs to the class itself; 4. Extends inherits through the prototype chain, and the underlying layer still uses the prototype mechanism. Class has not changed the essence of JavaScript prototype inheritance.

First, use npxstorybookinit to install and configure Storybook in the React project, run npmrunstorybook to start the local development server; 2. Organize component file structure according to functions or types, and create corresponding .stories.js files to define different states in each component directory; 3. Use Storybook's Args and Controls systems to achieve dynamic attribute adjustments to facilitate testing of various interactive states; 4. Use MDX files to write rich text documents containing design specifications, accessibility instructions, etc., and support MDX loading through configuration; 5. Define the design token through theme.js and use preview.js
