Build a CMS: nodePress

You have successfully created a flat filesystem Content Management System (CMS) using Go. The next step is to take the same ideal and make a web server using Node.js. I will show you how to load the libraries, create the server, and run the server.

This CMS will use the site data structure as laid out in the first tutorial, Building a CMS: Structure and Styling. Therefore, download and install this basic structure in a fresh directory.

Getting Node and the Node Libraries

The easiest way to install Node.js on a Mac is with Homebrew. If you haven’t installed Homebrew yet, the tutorial Homebrew Demystified: OS X’s Ultimate Package Manager will show you how.

To install Node.js with Homebrew, type this instruction into a terminal:

When done, you will have node and npm commands fully installed on your Mac. For all other platforms, follow the instructions on the Node.js website.

Be careful: Many package managers are currently installing Node.js version 0.10. This tutorial is assuming that you have version 5.3 or newer. You can check your version by typing:

The node command runs the JavaScript interpreter. The npm command is a package manager for Node.js to install new libraries, create new projects, and run scripts for a project. There are many great tutorials and courses on Node.js and NPM at Envato Tuts+.

To install the libraries for the web server, you have to run these commands in the Terminal.app or iTerm.app program:

Express is a web application development platform. It is similar to the goWeb library in Go. Handlebars is the templating engine for creating the pages. Moment is a library for working with dates. Marked is a great Markdown to HTML converter in JavaScript. Jade is an HTML shorthand language for easily creating HTML. Morgan is a middleware library for Express that generates the Apache Standard Log Files.

An alternative way to install the libraries is to download the source files for this tutorial. Once downloaded and unzipped, type this in the main directory:

That will install everything needed to create this project.

nodePress.js

Now you can get started creating the server. In the top directory of the project, create a file called nodePress.js, open it in your editor of choice, and start adding the following code. I am going to explain the code as it is placed into the file.

The server code starts with the initialization of all the libraries used to make the server. Libraries that do not have a comment with a web address are internal Node.js libraries.

Next, I set up all the global variables and library configurations. The use of global variables is not the best software design practice, but it does work and makes for quick development.

The parts variable is a hash array containing all the parts of a web page. Every page references the contents of this variable. It begins with the contents of the server.json file found at the top of the server directory.

I then use the information from the server.json file to create the complete paths to the styles and layouts directories used for this site.

Three variables are then set to null values: siteCSS, siteScripts, and mainPage. These global variables will contain all the CSS, JavaScripts, and main index page contents. These three items are the most requested items on any web server. Therefore, keeping them in memory saves time. If the Cache variable in the server.json file is false, these items get re-read with every request.

This block of code is for configuring the Marked library for generating HTML from Markdown. Mostly, I am turning on table and smartLists support.

The parts variable is further loaded with the parts from the styles and layout directories. Every file in the parts directory inside the site directory is also loaded into the parts global variable. The name of the file without the extension is the name used to store the contents of the file. These names get expanded in the Handlebars macro.

The next section of code defines the Handlebars helpers that I defined for use in the web server: save, date, and cdate. The save helper allows for the creation of variables inside a page. This version supports the goPress version where the parameter has the name and value together separated by a “|”. You can also specify a save using two parameters. For example:

This will produce the same results. I prefer the second approach, but the Handlebars library in Go doesn’t allow for more than one parameter.

The date and cdate helpers format the current date (date) or a given date (cdate) according to the moment.js library formatting rules. The cdate helper expects the date to render to be the first parameter and have the ISO 8601 format.

Now, the code creates an Express instance for configuring the actual server engine. The nodePress.use() function sets up the middleware software. Middleware is any code that gets served on every call to the server. Here, I set up the Morgan.js library to create the proper server log output.

This section of code defines all the routes needed to implement the web server. All routes run the setBasicHeader() function to set the proper header values. All requests for a page type will evoke the page() function, while all requests for post type page will evoke the posts() function.

The default for Content-Type is HTML. Therefore, for CSS, JavaScript, and images, the Content-Type is explicitly set to its appropriate value.

You can also define routes with the put, delete, and post REST verbs. This simple server only makes use of the get verb.

The last thing to do before defining the different functions used is to start the server. The server.json file contains the DNS name (here, it is localhost) and the port for the server. Once parsed, the server’s listen() function uses the port number to start the server. Once the server port is open, the script logs the address and port for the server.

The first function defined is the setBasicHeader() function. This function sets the response header to tell the browser to cache the page for one month. It also tells the browser that the server is a nodePress server. If there are any other standard header values you want, you would add them here with the response.append() function.

The page() function sends the layout template for a page and the location of the page on the server to the processPage() function.

The post() function is just like the page() function, except that posts have more items to define each post. In this series of servers, a post contains a type, category, and the actual post. The type is either blogs or news. The category is flatcms. Since these represent directory names, you can make them whatever you want. Just match the naming to what is in your file system.

The processPage() function gets the layout and the path to the page contents to render. The function starts by making a local copy of the parts global variable and adding the “contents” hashtag with the results of calling figurePage() function. It then sets the PageName hash value to the name of the page.

This function then compiles the page contents to the layout template using Handlebars. After that, the processShortCodes() function will expand all of the shortcodes defined on the page. Then, the Handlebars template engine goes over the code once more. The browser then receives the results.

The processShortCodes() function takes the web page contents as a string and searches for all shortcodes. A shortcode is a block of code similar to HTML tags. An example would be:

This code has a shortcode for box around an HTML paragraph. Where HTML uses < and >, shortcodes use -[ and ]-. After the name, a string containing arguments to the shortcode can or cannot be there.

The processShortCodes() function finds a shortcode, gets its name and arguments, finds the end to get the contents, processes the contents for shortcodes, executes the shortcode with the arguments and contents, adds the results to the finished page, and searches for the next shortcode in the rest of the page. The looping is performed by recursively calling the function.

This next section defines the shortcodes json structure that defines the name of a shortcode associated to its function. All shortcode functions accept two parameters: args and inside. The args is everything after the name and space and before the closing of the tag. The inside is everything contained by the opening and closing shortcode tags. These functions are basic, but you can create a shortcode to perform anything you can think of in JavaScript.

The figurePage() function receives the full path to a page on the server. This function then tests for it to be an HTML, Markdown, or Jade page based on the extension. I am still using .amber for Jade since that was the library I used with the goPress server. All Markdown and Jade contents get translated into HTML before passing it on to the calling routine. Since the Markdown processor translates all quotes to &quot;, I translate them back before passing it back.

The fileExists() function is a replacement for the fs.exists() function that used to be a part of the fs library of Node.js. It uses the fs.statSync() function to try to get the status of the file. If an error happens, a false is returned. Otherwise, it returns true.

The last function is the MergeRecursive() function. It copies the second pass object into the first passed object. I make use of this to copy the main parts global variable into a local copy before adding page-specific parts.

Running Locally

After saving the file, you can run the server with:

Alternatively, you can use the npm script that is in the package.json file. You run npm scripts like this:

This will run the start script that is inside the package.json file.

nodePress Server Main Page
nodePress Server Main Page

Point your web browser to http://localhost:8080 and you will see the page above. You might have noticed that I added more test code to the main page. All the changes to the pages are in the download for this tutorial. They're mostly just some minor tweaks to more completely test the functionality and to fit any differences from using different libraries. The most notable difference is that the Jade library doesn't use $ to name variables while Amber does.

Conclusion

Now you have exactly the same flat filesystem CMS in Go and Node.js. This only scratches the surface of what you can build with this platform. Experiment and try something new. That is the best part of creating your own web server.

Tags:

Comments

Related Articles