Creating an express.js app using YUI modown modules

Overview

modown is a recent project from the Yahoo Mojito team. Rather than being a framework, modown is a collection of building blocks designed to work together. The goal is to make it easier to build single page applications (SPA) through the combination of modularity and the flexibility.

It also incorporates the ability to run the same components on both the server and the client, allowing you to render the initial state on the server and then let the browser take over.

The project was presented at YUIConf2013 and although I wasn’t able to attend the talk I could immediately tell from the slides that the concepts behind this project are going to simplify my development process while also providing a lot of power.

This post breaks down how to incorporate these principles and modules. As always, this post exists for me to learn the process and it is possible that there are mistakes or inferences that need to be corrected.

It assumes that you are already familiar with the basics of setting up an express.js application

To create this post, I relied very heavily on the current documentation which consists of:

Caridy’s slides introducing the modown project
The express modown modules in npm

and component level examples on git hub, such as express-yui

Generate your basic express application

Generate your express application:

Replacing bakery_express with the name of your application.

Now, install the basic dependencies and make sure that our express app runs:

Now that we have verified that we have a working express.js application we should update the package.json file so that it reflects the correct name for or our application. It is good to set this from the start because, later in the process, some of the YUI components will generate directories based upon the name of our application.

Setting the application name field

Now we are ready to start building out our application. The first thing that I like to do is add a configuration module to our express app. While this is not necessary for the modown components to work I find that separating out configuration from code simplifies my life especially if I do it from the start.

Adding a configuration module

To provide configuration data to express apps, I create a self contained module called config. My application then requires the config module. The config module exports a dictionary of configuration settings for my application. This could be handled by directly reading a json file but adding the extra level of indirection allows some aspects of the configuration to be determined procedurally. There are some examples of where this is advantageous a little further in.

Step 1 — step up the module structure:

    • config
      • index.js
      • config.json

Step 2 — update config.json

We don’t (yet) have a lot of configuration for our app, but we can start with a few simple defaults, such as the name of the app, and the port we would like the app to be mounted on:

Step 3 — update index.js so that it reads the config file and returns the dictionary.

At this stage, the module only needs to read the config.json file and return it’s contents. As we increase the complexity of the app, we will add some procedural methods to the config file but for now our index.js really only consists of two lines:

Step 4 — require our configuration module

The final step is using the configuration module, so add this line to app.js:
var config = require("./config");

This should be done fairly early in the file probably in the same area that you do the rest of your requires.

Now we can start using the configuration data. Let’s use the configuration data to set the application name and the port to use. Find the line where the express app currently sets the port number:

app.set('port', process.env.PORT || 3000);

and replace it with:

app.set('name', config.name);
app.set('port', config.port);

As we recall from the express documentation on app.set, this function assigns a value to a variable on the application instance. So, we are looking up the name of the app in our configuration file and setting it on the app instance. We are doing the same thing for the port.

Preserving the ability to override the port via environment variable

Now we are ready to start incorporating the modown modules into our express app.

Incorporating express-yui

express-yui is responsible for extending the express app to support yui. First add express-yui to your app’s dependencies and install it. Now we are ready to extend the express app.

Open up app.js and find the line where you required the config module and add

var expyui = require('express-yui');

Then shortly after line that instances the express app add:

At this stage the express app is ready to use any of the standard YUI modules on the server. For example, if our bakery wanted to suggest a refreshment to accompany your cookie based upon the current temperature we can use YQL to look up the weather and use that information to choose what refreshment to pass to the page template for rendering.

To accomplish this, lets add a new route to our app. We will call this route suggestions. Following the examples for the index and user list modules we will create a new file in the routes subdirectory that provides this functionality. Remember our goal is keep our building blocks small and readable and keeping the routes in separate files makes it easier to maintain and debug.

We are going to call this routes file: cookies.js. All routes that relate to cookies will be placed in this file. For now, we will only export one route function; the suggestions route.

To expose this route to our express application we require our cookies:

Then, closer to the point where the the app creates the server we will add a call setting up a path to this route:

expyui.expose() does a few things. First on the server side, it ensures that the request object is properly configured to find and access YUI. Second and on the client side, it’ll setup a property called state that can be used in your templates to setup and use YUI through your server. We’ll look at an example of this when we get to client side yui.

Making yui globally available to all routes
Before implementing yui on the client side I am going to convert our view engine from jade to handlebars. YUI embedding through jade should work fine, but I happen to know handlebars better than I know jade and so it is easier for me.

Switching your view engine from jade to handlebars

This section is functionally independent of using YUI within express; in other words it has zero dependence on using YUI and vice versa. Switching your view engine is relatively simple with: express3-handlebars

Step 1: add express3-handlebars to your package dependencies

"express3-handlebars": "*"

If you have jade currently installed in your package dependencies you will want to remove it.

Step 2: Install

Following standard node practices, we install express3-handlebars locally.

npm install

Step 3: Require the package

In app.js, add the require for handlebars.

var exphbs = require('express3-handlebars')

Step 4: Set the view engine

Still in app.js you will need to set the engine and enable it. This should be done before you set any routes.

app.engine registers exphbs as the function to be called whenever express attempts to render a template ending in .handlebars. The defaultLayout argument specifies that handlebars will use main.handlebars as our primary layout for our page.

app.set(‘view engine’, ‘handlebars’) instructs express to use handlebars as the file extension if res.render is called on a view that does not include an extension.

Step 5: Setup our handlebars views

Handlebars makes it easy to separate the primary layout of our page from the view of the data. We will create a “main” template that describes structure and a separate template for the view.

Page layouts will be stored in a ‘layouts’ subdirectory of the views directory. Views will be stored in the views directory. The directory structure looks something like this:

    • views
      • cookieSuggestions.handlebars
      • test.handlebars
      • layouts
    • main.handlebars

The layouts directory contains full page templates. Any handlebars templates stored at the views level are partial templates and will be folded into the layouts by handlebars.  For example, a very very basic main.handlebars template would look something like this:

When we ask handlebars to render cookieSuggestions.handlebars it will render main.handlebars replacing {{{body}}} with the rendered contents of cookieSuggestions.handlebars

We are going to usetest.handlebars to make sure that our template rendering is working. It’s contents can be anything but I tend to use: <h1> This is a test template </h1>

To verify that handlebars is working, add this route to your app.js:

Now fire up your app and go to the /test route. You should see “This is a test template”.
The above example allows us to integrate YUI, handlebars and express while maintaining a functional separation between yui and the templates. There is another modown component locator-handlebars that faciliates the sharing of handlebar templates between the server and the client, but that is a topic for another tutorial.

Using yui on the client side

Now that we have handlebars working in our app, lets set up the cookieSuggestions template so that it:

  1. Accepts data from the server that specifies the location and temperature
  2. Uses yui on the client side to retrieve some additional information

Our basic template looks like this:

Three things are happening in this template. First, the server will replace the template tags with content supplied to the template engine.

Second, we add a unique html element to our page. This html element will be used to hold the result of our client side query.

Third, on the client side, we ask yui to load two modules; yql and node. Once those modules are loaded yui will call getTempForZipCode, passing it the instance of our YUI object and a zipcode. getTempForZipCode then calls yql with a query, passing the result to a callback. The call back retrieves the temperature from the result. Then it uses the node module to locate the element with the clientResult id and stores a string in that elements html.

The final step is to adjust our app to look to use our new view. This means changing app.js to include a new route and cookies.js to expose a new route that renders the view. The change to app.js is pretty simple. We will add this line:

app.get('/suggestions/template', expyui.expose(), cookies.suggestionsTemplate);

We use a separate route and code path so that it is easy to compare the two methods. In routes/cookies.js we will export suggestionsTemplate method. The core of which looks like this:

The key difference is that instead of using res.send, we use res.render.
res.render is responsible rendering the template.

At this stage we have setup and implemented yui on the server and on the client from a local express app.

If you want to go a little further and start serving custom YUI modules from within your express app we will need to incorporate at least two more modown modules.

Custom YUI modules from within express

We add support for custom YUI modules using the locator and locator-yui modules. The locator module provides a level of abstraction between file names and their actual location on disk and locator-yui is an extension/plugin to locator that is used specifically for yui.

Start by installing these packages in your app.

package.json

Next, require locator and locator-yui.

Why does this look different

Now, before you extend the express app, create an instance of the locator and expose that instance to the app.

Finally, extend our locator instance with an instance of locator-yui.

Everything is now setup to use custom YUI modules on the client and server side. Let’s take a look at a simple YUI module.

Setting up a custom YUI module for your express app

We are going to create an extension of yui’s model class to represent a cookie. This class will be called CookieModel and will have four attributes:

  1. type: — what kind of cookie
  2. temperatureCutoff: — the temperature at which to change the suggestion
  3. belowCutoffSuggestion: — suggestion for what to drink with the cookie when the temperature is below the cutoff
  4. aboveCutoffSuggestion: — suggestion for what to drink with the cookie when the temperature is above the cutoff

The first question to ask, is where to place your YUI modules. Thankfully, this is more a matter of preference then requirement. The locator module will find our yui modules and ensure that they are built (using shifter) and placed in proper sub directories of the build directory. For this particular example, I will put the new module inside of a subdirectory called: yui_modules.

We are going to setup the module using the same techniques that I illustrated in Using shifter to build a yui module.

So start by creating this structure:

    • cookie-model
      • src
        • cookie-model
          • build.json
          • js
            • cookie.model.js

Since this is a pretty simple model, our build.json file will be very bare bones:

We will also keep our CookieModel class very simple.

For a good explanation of what is going on here, please see YUI’s model documentation. Most of the heavy lifting of this class will be done by the model class. The only real augmentation is to add the getSuggestion method which looks at the temperature and returns a suggestion.

Before you wire this into your express app, make sure that the model actually compiles by changing directories into the same directory as the build.json and run shifter. Once you are sure you are generating valid javascript you can start wireing it into your express app.

This involves:

Updating or creating a route to use the new model
Updating or creating the template that we will render
Updating or creating a new path to our route

For this example we will add a new route: suggestionsModel that uses our new model. We will also create a cookieSuggestionsModel.handlebars template to use the model and a new path called cookie/model to access this new template.

The new export for suggestionsModel looks like this:

and our new template looks like this:

And our new path is:

Finally, we need to make sure that yui is properly loaded on our server before we start listening for http requests. To do this, we will add a call to app.yui.ready that will prepare our modules for use and then emit a signal when yui is ready.

And finally, we will change the http.createServer line so that it is only called after the ‘ready’ signal is emitted:

Congratulations, you have now implemented a fully functional express/yui app based upon modown modules. While there is a lot more work to do to make this a production level app, such as serving modules from a dedicated cdn instead of from your app, I hope this gave you a good idea about how the pieces fit together.

LevelsOfThinking