More Features

NodeAtlas offers also a large set of features for development or packaging with the configuration sytem. We will see that.

Manage routing (URL Rewriting)

Although you can configure static urls, you can also set of dynamic url!

Standard

With the following configuration:

{
    "routes": {
        "/list-of-members/:member/": {
            "template": "members.htm"
        },
        "/list-of-members/": {
            "template": "members.htm"
        },
        "/": {
            "template": "index.htm"
        }
    }
}

you can access:

and retrieve the :member value inchangeVariation (common and specific).

exports.changeVariation = function (params, next) {
    var variation = params.variation;

    console.log(variation.params.member);
    // \> 'toto', 'bob-eponge99', 'node-atlas' or 'etc'.

    next(variation);
}

Dynamic url creation rules are those of Express.js.

Regular Expressions

You can also enable regular expressions to a specific path with regExp. If it is true, the previous profile no longer works and you pass in Regular Expression mode. If regExp is a string, it acts as a flag (g, i, m or y).

See the following configuration:

{
    "routes": {
        "/list-of-members/([-a-z0-9]+)/?": {
            "template": "members.htm",
            "regExp": "g"
        },
        "/list-of-members/?": {
            "template": "members.htm",
            "regExp": true
        },
        "/": {
            "template": "index.htm"
        }
    }
}

you can access:

and retrieve the ([-a-z0-9] +) value in the changeVariation (common and specific).

exports.changeVariation = function (params, next) {
    var variation = params.variation;

    if (variation.params && variation.params[0]) { variation.params.member = variation.params[0]; }
    // variation.params[1] for second match, etc...

    console.log(variation.params.member);
    // \> 'toto', 'bob-eponge99', 'node-atlas' or 'etc'.

    next(variation);
}

The rules for creating dynamic url with regExp are those of RegExpJavaScript.

Routing in a shared file

In order to not rewrite a long route list in webconfig.json file to your development environment andwebconfig.prod.json to your production environment, you can group route in a file of your choice. By convention, the name is routes.json file.

For example:

The following set of file

├─ templates/
│  └─ index.htm
├─ webconfig.json
└─ webconfig.prod.json

with webconfig.json

{
    "httpPort": 7777,
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

and with webconfig.prod.json

{
    "httpPort": 7776,
    "httpHostname": "blog.lesieur.name",
    "urlPort": 80,
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

could be the following set of file

templates/
— index.htm
routes.json
webconfig.json
webconfig.prod.json

with webconfig.json

{
    "httpPort": 7777,
    "routes": "routes.json"
}

with webconfig.prod.json

{
    "httpPort": 7776,
    "httpHostname": "blog.lesieur.name",
    "urlPort": 80,
    "routes": "routes.json"
}

and routes.json

{
    "/": {
        "template": "index.htm"
    }
}

Note : You can create multiple route file as routes.en.json and routes.fr.json and associate each of them in a set of webconfig parameterize to run a website in various languages.

Manage a page not found

Listen all urls, and also file provide by assetsRelativePath

To display a custom page when a resource is not found you must:

  1. Prepare a 404 page.
  2. Fill the parameter with pageNotFound with the following value : key of the prepared 404 page.

See the example below:

{
    "pageNotFound": "/not-found-page/",
    "routes": {
        "/list-of-members/": {
            "template": "members.htm"
        },
        "/": {
            "template": "index.htm"
        },
        "/not-found-page/": {
            "template": "error.htm",
            "statusCode": 404
        }
    }
}

you can access to:

Multilingual Error Page

For this, just create a new route with * at the end with the languageCode.

See below :

{
    "pageNotFound": "/not-found-page/",
    "languageCode": "en-gb",
    "routes": {
        "/list-of-members/": {
            "template": "members.htm",
            "variation": "members.json"
        },
        "/": {
            "template": "index.htm",
            "variation": "index.json"
        },
        "/not-found-page/": {
            "template": "error.htm",
            "variation": "error.json",
            "statusCode": 404
        },
        "/francais/liste-des-membres/": {
            "template": "members.htm",
            "languageCode": "fr-fr",
            "variation": "members.json"
        },
        "/francais/": {
            "template": "index.htm",
            "languageCode": "fr-fr",
            "variation": "index.json"
        },
        "/francais/*": {
            "template": "error.htm",
            "languageCode": "fr-fr",
            "variation": "error.json",
            "statusCode": 404
        }
    }
}

Manage redirects

To go to a different address (redirect 301 or 302) when you get to a url you must use the redirect parameter.

Note : if you don't set statusCode, no redirect will be executed. The statusCode is mandatory for redirection.

Static

See the example below:

{
    "routes": {
        "/list-of-members/": {
            "template": "members.htm"
        },
        "/list-of-members": {
            "redirect": "/list-of-members/",
            "statusCode": 301
        },
        "/go-to-node-atlas/": {
            "redirect": "https://node-atlas.js.org/",
            "statusCode": 302
        },
        "/": {
            "template": "index.htm"
        }
    }
}

You will be redirected:

Dynamic

See the example below:

{
    "routes": {
        "/list-of-members/:member/": {
            "template": "members.htm"
        },
        "/list-of-members/:member": {
            "redirect": "/membres/:member/",
            "statusCode": 301
        },
        "/": {
            "template": "index.htm"
        }
    }
}

You will be redirected to http://localhost/list-of-members/haeresis/ when you access to http://localhost/list-of-members/haeresis with a header permanent redirect.

With regular expressions

See the example below:

{
    "routes": {
        "/membres/([-a-z0-9]+)/": {
            "template": "members.htm",
            "regExp": true
        },
        "/list-of-members/([-a-z0-9]+)/": {
            "redirect": "/membres/$0/",
            "statusCode": 301,
            "regExp": true
        },
        "/list-of-members/": {
            "template": "members.htm"
        },
        "/": {
            "template": "index.htm"
        }
    }
}

You will be redirected to http://localhost/list-of-members/haeresis/ when you access to http://localhost/list-of-members/haeresis with a header permanent redirect.

For the second match use $1, the third $2, etc.

Manage Headers

By défault, sent Headers by NodeAtlas are followings: Content-Type:text/html; charset=utf-8 with a 200 statusCode.

It's possible to modify this values for a specific route (for local API for example).

{
    "routes": {
        "/api/articles": {
            "template": "display-json.htm",
            "controller": "blog/list-of-articles.js",
            "mimeType": "application/json"
            "charset": "ISO-8859-1",
            "statusCode": 203
        }
    }
}

It's also possible to modify all Headers values, this erase all shortcuts before, except the statusCode.

{
    "routes": {
        "/api/articles": {
            "template": "display-json.htm",
            "controller": "blog/list-of-articles.js",
            "statusCode": 203,
            "headers": {
                "Content-Type": "application/json; charset=utf-8",
                "Access-Control-Allow-Origin": "*"
            }
        }
    }
}

Run Website with HTTPs

It is very simple to run an instance of NodeAtlas with HTTPs protocol. You just have to create such a security folder in which to place your server.key and server.crt file to supply the protocol.

Just use the following configuration:

{
    "httpSecure": true,
    "httpSecureRelativeKeyPath": "security/server.key",
    "httpSecureRelativeCertificatePath": "security/server.crt",
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

Alternatively , if your two Key and Certificate files have the same name, use this configuration:

{
    "httpSecure": "security/server",
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

This is also possible to just set the httpSecure value to true for get a "https" like urlBasePath or urlBase in your paths variables. But the server will not running in HTTPs and you will validate certificate by your own other way (with a server proxy for example).

{
    "httpSecure": true,
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

Note : in production, if you use a proxy for redirect request/response, don't forget use urlPort: 443 instead of urlPort: 80 for HTTPs.

Minify CSS / JS

You can automatically generate CSS and JS files minified and obfuscated by creating Bundles by referencing the file by input and output path. Of course you can do as much as you want. The gereration files is execute every time you start NodeAtlas either as a server or via the --generate command if a Bundle exists in the Webconfig.

Creating Bundles

With the following configuration:

{
    "bundles": {
        "javascript": {
            "javascript/boot.min.js": [
                "javascript/modernizr.js",
                "javascript/yepnot.js",
                "javascript/html5Shiv.js"
            ],
            "javascript/framework.min.js": [
                "javascript/jquery.js",
                "javascript/jquery-ui.js",
                "javascript/prettify.js",
                "javascript/prettify/run_prettify.js"
            ],
            "javascript/common.min.js": [
                "javascript/components/extended-format-date.js",
                "javascript/common.js"
            ]
        },
        "stylesheets": {
            "stylesheets/common.min.css": [
                "stylesheets/common.css",
                "stylesheets/common-min780.css",
                "stylesheets/common-min1160.css"
            ]
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

and the following set of file:

├─ assets/
│  ├─ stylesheets/
│  │  ├─ common.css
│  │  ├─ common-min780.css
│  │  └─ common-min1160.css
│  └─ javascript/
│     ├─ modernizr.js
│     ├─ yepnot.js
│     ├─ html5Shiv.js
│     ├─ jquery.js
│     ├─ jquery-ui.js
│     ├─ prettify.js
│     ├─ prettify/
│     │  └─ run_prettify.js
│     ├─ components/
│     │  └─ extended-format-date.js
│     └─ common.js
├─ templates/
│  └─ index.htm
└─ webconfig.json

you will get the following new files:

├─ assets/
│  ├─ stylesheets/
│  │  ├─ common.css
│  │  ├─ common-min780.css
│  │  ├─ common-min1160.css
│  │  └─ common.min.css     ⤆ new file
│  └─ javascript/
│     ├─ modernizr.js
│     ├─ yepnot.js
│     ├─ html5Shiv.js
│     ├─ jquery.js
│     ├─ jquery-ui.js
│     ├─ prettify.js
│     ├─ prettify/
│     │  └─ run_prettify.js
│     ├─ components/
│     │  └─ extended-format-date.js
│     ├─ common.js
│     ├─ boot.min.js        ⤆ new file
│     ├─ framework.min.js   ⤆ new file
│     └─ common.min.js      ⤆ new file
├─ templates/
│  └─ index.htm
└─ webconfig.json

Bundles in a shared file

In order to not re-write a long Bundles configuration list in webconfig.json file to your development environment andwebconfig.prod.json to your production environment, you can group routes in a file of your choice. By convention, the name is bundles.json file.

For example:

The following set of file

├─ assets/
│  ├─ stylesheets/
│  │  ├─ common.css
│  │  ├─ common-min780.css
│  │  └─ common-min1160.css
│  └─ javascript/
│     ├─ modernizr.js
│     ├─ yepnot.js
│     ├─ html5Shiv.js
│     ├─ jquery.js
│     ├─ jquery-ui.js
│     ├─ prettify.js
│     ├─ prettify/
│     │  └─ run_prettify.js
│     ├─ components/
│     │  └─ extended-format-date.js
│     └─ common.js
├─ templates/
│  └─ index.htm
├─ webconfig.json
└─ webconfig.prod.json

with webconfig.json

{
    "httpPort": 7777,
    "bundles": {
        "javascript": {
            "javascript/boot.min.js": [
                "javascript/modernizr.js",
                "javascript/yepnot.js",
                "javascript/html5Shiv.js"
            ],
            "javascript/framework.min.js": [
                "javascript/jquery.js",
                "javascript/jquery-ui.js",
                "javascript/prettify.js",
                "javascript/prettify/run_prettify.js"
            ],
            "javascript/common.min.js": [
                "javascript/components/extended-format-date.js",
                "javascript/common.js"
            ]
        },
        "stylesheets": {
            "stylesheets/common.min.css": [
                "stylesheets/common.css",
                "stylesheets/common-min780.css",
                "stylesheets/common-min1160.css"
            ]
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

and with webconfig.prod.json

{
    "httpPort": 7776,
    "httpHostname": "blog.lesieur.name",
    "urlPort": 80,
    "bundles": {
        "javascript": {
            "javascript/boot.min.js": [
                "javascript/modernizr.js",
                "javascript/yepnot.js",
                "javascript/html5Shiv.js"
            ],
            "javascript/framework.min.js": [
                "javascript/jquery.js",
                "javascript/jquery-ui.js",
                "javascript/prettify.js",
                "javascript/prettify/run_prettify.js"
            ],
            "javascript/common.min.js": [
                "javascript/components/extended-format-date.js",
                "javascript/common.js"
            ]
        },
        "stylesheets": {
            "stylesheets/common.min.css": [
                "stylesheets/common.css",
                "stylesheets/common-min780.css",
                "stylesheets/common-min1160.css"
            ]
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

could be the following set of file

├─ assets/
│  ├─ stylesheets/
│  │  ├─ common.css
│  │  ├─ common-min780.css
│  │  └─ common-min1160.css
│  └─ javascript/
│     ├─ modernizr.js
│     ├─ yepnot.js
│     ├─ html5Shiv.js
│     ├─ jquery.js
│     ├─ jquery-ui.js
│     ├─ prettify.js
│     ├─ prettify/
│     │  └─ run_prettify.js
│     ├─ components/
│     │  └─ extended-format-date.js
│     └─ common.js
├─ templates/
│  └─ index.htm
├─ bundles.json              ⤆ new file
├─ webconfig.json
└─ webconfig.prod.json

with webconfig.json

{
    "httpPort": 7777,
    "bundles": "bundles.json",
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

with webconfig.prod.json

{
    "httpPort": 7776,
    "httpHostname": "blog.lesieur.name",
    "urlPort": 80,
    "bundles": "bundles.json",
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

and bundles.json

{
    "javascript": {
        "javascript/boot.min.js": [
            "javascript/modernizr.js",
            "javascript/yepnot.js",
            "javascript/html5Shiv.js"
        ],
        "javascript/framework.min.js": [
            "javascript/jquery.js",
            "javascript/jquery-ui.js",
            "javascript/prettify.js",
            "javascript/prettify/run_prettify.js"
        ],
        "javascript/common.min.js": [
            "javascript/components/extended-format-date.js",
            "javascript/common.js"
        ]
    },
    "stylesheets": {
        "stylesheets/common.min.css": [
            "stylesheets/common.css",
            "stylesheets/common-min780.css",
            "stylesheets/common-min1160.css"
        ]
    }
}

Note : it is possible to disable Bundles by not including them in the webconfig.

Disable Bundles

It is also possible to not execute the minification when run a website with NodeAtlas with "stylesheetsBundlesEnable": false et `"javascriptBundlesEnable": false`` for each type of Bundle.

{
    "stylesheetsBundlesEnable": false,
    "javascriptBundlesEnable": false,
    "bundles": {
        "javascript": {
            "javascript/boot.min.js": [
                "javascript/modernizr.js",
                "javascript/yepnot.js",
                "javascript/html5Shiv.js"
            ],
            "javascript/framework.min.js": [
                "javascript/jquery.js",
                "javascript/jquery-ui.js",
                "javascript/prettify.js",
                "javascript/prettify/run_prettify.js"
            ],
            "javascript/common.min.js": [
                "javascript/components/extended-format-date.js",
                "javascript/common.js"
            ]
        },
        "stylesheets": {
            "stylesheets/common.min.css": [
                "stylesheets/common.css",
                "stylesheets/common-min780.css",
                "stylesheets/common-min1160.css"
            ]
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

Note : if your bundle is in shared file, you could desactivated it also without the "bundles": "bundles.json". Just remove it.

Re-generate Bundles before each Page Response

For test your page with minified files, you can ask it to be regenerated before each page response with "stylesheetsBundlesBeforeResponse": false et `"javascriptBundlesBeforeResponse": false`` for each type of Bundle.

{
    "stylesheetsBundlesBeforeResponse": false,
    "javascriptBundlesBeforeResponse": false,
    "bundles": {
        "javascript": {
            "javascript/boot.min.js": [
                "javascript/modernizr.js",
                "javascript/yepnot.js",
                "javascript/html5Shiv.js"
            ],
            "javascript/framework.min.js": [
                "javascript/jquery.js",
                "javascript/jquery-ui.js",
                "javascript/prettify.js",
                "javascript/prettify/run_prettify.js"
            ],
            "javascript/common.min.js": [
                "javascript/components/extended-format-date.js",
                "javascript/common.js"
            ]
        },
        "stylesheets": {
            "stylesheets/common.min.css": [
                "stylesheets/common.css",
                "stylesheets/common-min780.css",
                "stylesheets/common-min1160.css"
            ]
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

Note : this is not recommanded for production environment because it's slowed responses pages.

CSS generation with Less

You can use the preprocessor Less to create your CSS. The operation is as follows: whenever a CSS request is made, if a Less equivalent exists it is read and it generates the CSS. Once done, the new CSS is responded.

With the following structure:

├─ assets/
│  └─ stylesheets
│     └─ common.less
├─ templates/
│  └─ index.htm
└─ webconfig.json

and the following webconfig:

{
    "enableLess": true,
    "routes": {
        "/": "index.htm"
    }
}

and the following content in:

templates/index.htm

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Less Test</title>
        <link rel="stylesheet" href="stylesheets/common.css">
    </head>
    <body>
        <p>This line is red.</p>
    </body>
</html>

assets/stylesheets/common.less

p {
    color: #f00;
}

you will build the assets/stylesheets/common.css by calling the url http://localhost/ or http://localhost/stylesheets/common.css.

Source Map and Minification

By default, in the above example, a common.css.map file will be generated. This allows your browser to indicated you that line in .less file has generated the CSS property of the item you have selected in your debugger.

Disable this with enableLess.sourceMap to false:

    "enableLess": {
        "sourceMap": false
    },
    "routes": {
        "/": "index.htm"
    }

You can also generate CSS files already minify with:

    "enableLess": {
        "compress": true
    },
    "routes": {
        "/": "index.htm"
    }

Compile Less files with --generate

Because of Less are compilated on the fly, when a file is requested in http(s), modification needed running website for generate CSS output. Then you can use CSS. It's possible to skip running step and directly complated Less before minify CSS with enableLess.less.

With the following webconfig.json:

{
    "enableLess": {
        "less": [
            "stylesheets/common.less",
            "stylesheets/component-1.less",
            "stylesheets/component-2.less",
            "stylesheets/component-3.less"
        ]
    },
    "routes": {
        "/": "index.htm"
    }
}

or with the following webconfig.json:

{
    "enableLess": {
        "less": "less.json"
    },
    "routes": {
        "/": "index.htm"
    }
}

with less.json containing :

[
    "stylesheets/common.less",
    "stylesheets/component-1.less",
    "stylesheets/component-2.less",
    "stylesheets/component-3.less"
]

The @import used by Less will be capable to walk into subdirectories : styles, stylesheets or css. It's possible to change that with :

{
    "enableLess": {
        "paths": [
            "subdirectory/styles-files",
        ],
        "less": "less.json"
    },
    "routes": {
        "/": "index.htm"
    }
}

CSS generation with Stylus

You can use the preprocessor Stylus to create your CSS. The operation is as follows: whenever a CSS request is made, if a Stylus equivalent exists it is read and it generates the CSS. Once done, the new CSS is responded.

With the following structure:

├─ assets/
│  └─ stylesheets
│     └─ common.styl
├─ templates/
│  └─ index.htm
└─ webconfig.json

and the following webconfig:

{
    "enableStylus": true,
    "routes": {
        "/": "index.htm"
    }
}

and the following content in:

templates/index.htm

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Stylus Test</title>
        <link rel="stylesheet" href="stylesheets/common.css">
    </head>
    <body>
        <p>This line is red.</p>
    </body>
</html>

assets/stylesheets/common.styl

p
    color: #f00

you will build the assets/stylesheets/common.css by calling the url http://localhost/ or http://localhost/stylesheets/common.css.

Source Map and Minification

By default, in the above example, a common.css.map file will be generated. This allows your browser to indicated you that line in .styl file has generated the CSS property of the item you have selected in your debugger.

Disable this with enableLess.sourceMap to false:

    "enableStylus": {
        "sourceMap": false
    },
    "routes": {
        "/": "index.htm"
    }

You can also generate CSS files already minify with:

    "enableStylus": {
        "compress": true
    },
    "routes": {
        "/": "index.htm"
    }

Note: More options on stylus documentation for module.

Compile Stylus files with --generate

Because of Stylus are compilated on the fly, when a file is requested in http(s), modification needed running website for generate CSS output. Then you can use CSS. It's possible to skip running step and directly complated Stylus before minify CSS with enableLess.stylus.

With the following webconfig.json:

{
    "enableLess": {
        "stylus": [
            "stylesheets/common.styl",
            "stylesheets/component-1.styl",
            "stylesheets/component-2.styl",
            "stylesheets/component-3.styl"
        ]
    },
    "routes": {
        "/": "index.htm"
    }
}

or with the following webconfig.json:

{
    "enableLess": {
        "stylus": "stylus.json"
    },
    "routes": {
        "/": "index.htm"
    }
}

with stylus.json containing :

[
    "stylesheets/common.styl",
    "stylesheets/component-1.styl",
    "stylesheets/component-2.styl",
    "stylesheets/component-3.styl"
]

The @import used by Less will be capable to walk into subdirectories : styles, stylesheets or css. It's possible to change that with :

{
    "enableLess": {
        "paths": [
            "subdirectory/styles-files",
        ],
        "stylus": "stylus.json"
    },
    "routes": {
        "/": "index.htm"
    }
}

Optimize Images files

You can automatically generate optimized images files by creating Optimizations by referencing the file by input and output path. Of course you can do as much as you want. The optimization files is execute every time you start NodeAtlas either as a server or via the --generate command if an Optimization exists in the Webconfig.

Creating Optimizations

With the following configuration:

{
    "optimizations": {
        "images": {
            "media/images/example.png": "media/images/optimized/",
            "media/images/example.jpg": "media/images/optimized/",
            "media/images/example.gif": "media/images/optimized/",
            "media/images/example.svg": "media/images/optimized/"
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

and the following set of file:

├─ assets/
│  └─ media/
│     └─ images/
│        ├─ example.png
│        ├─ example.jpg
│        ├─ example.gif
│        └─ example.svg
├─ templates/
│  └─ index.htm
└─ webconfig.json

you will get the following new files:

├─ assets/
│  └─ media/
│     └─ images/
│        ├─ example.png
│        ├─ example.jpg
│        ├─ example.gif
│        ├─ example.svg
│        └─ optimized/       ⤆ new folder
│           ├─ example.png   ⤆ new file
│           ├─ example.jpg   ⤆ new file
│           ├─ example.gif   ⤆ new file
│           └─ example.svg   ⤆ new file
├─ templates/
│  └─ index.htm
└─ webconfig.json

Create Optimizations by group of file

For example, not define file one by one, but in group:

{
    "optimizations": {
        "images": {
            "media/images/*.{gif,jpg,png,svg}": "media/images/optimized/"
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

Add more options to Optimizations

It is possible to redefine default options used for optimizations via this 4 objects:

{
    "optimizations": {
        "jpg": { "progressive": false },
        "gif": { "interlaced": false },
        "png": { "optimizationLevel": 1 },
        "svg": { "multipass": false },
        "images": {
            "media/images/*.{gif,jpg,png,svg}": "media/images/optimized/"
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

To know all options it is here:

Optimizations in a shared file

In order to not re-write a long Bundles configuration list in webconfig.json file to your development environment andwebconfig.prod.json to your production environment, you can group files in a file of your choice. By convention, the name is optimizations.json file.

For example:

The following set of file

├─ assets/
│  └─ media/
│     └─ images/
│        ├─ example.png
│        ├─ example.jpg
│        ├─ example.gif
│        └─ example.svg
├─ templates/
│  └─ index.htm
├─ webconfig.json
└─ webconfig.prod.json

with webconfig.json

{
    "httpPort": 7777,
    "optimizations": {
        "images": {
            "media/images/example.png": "media/images/optimized/",
            "media/images/example.jpg": "media/images/optimized/",
            "media/images/example.gif": "media/images/optimized/",
            "media/images/example.svg": "media/images/optimized/"
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

and with webconfig.prod.json

{
    "httpPort": 7776,
    "httpHostname": "blog.lesieur.name",
    "urlPort": 80,
    "optimizations": {
        "images": {
            "media/images/example.png": "media/images/optimized/",
            "media/images/example.jpg": "media/images/optimized/",
            "media/images/example.gif": "media/images/optimized/",
            "media/images/example.svg": "media/images/optimized/"
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

could be the following set of file

├─ assets/
│  └─ media/
│     └─ images/
│        ├─ example.png
│        ├─ example.jpg
│        ├─ example.gif
│        └─ example.svg
├─ templates/
│  └─ index.htm
├─ bundles.json
├─ webconfig.json
└─ webconfig.prod.json

with webconfig.json

{
    "httpPort": 7777,
    "optimizations": "optimizations.json",
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

with webconfig.prod.json

{
    "httpPort": 7776,
    "httpHostname": "blog.lesieur.name",
    "urlPort": 80,
    "optimizations": "optimizations.json",
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

and optimizations.json

{
    "images": {
        "media/images/example.png": "media/images/optimized/",
        "media/images/example.jpg": "media/images/optimized/",
        "media/images/example.gif": "media/images/optimized/",
        "media/images/example.svg": "media/images/optimized/"
    }
}

Note : it is possible to disable Optimizations by not including them in the webconfig.

Disable Optimizations

It is also possible to not execute the optimization when run a website with NodeAtlas with "imagesOptimizationsEnable": false.

{
    "imagesOptimizationsEnable": false,
    "optimizations": {
        "images": {
            "media/images/example.png": "media/images/optimized/",
            "media/images/example.jpg": "media/images/optimized/",
            "media/images/example.gif": "media/images/optimized/",
            "media/images/example.svg": "media/images/optimized/"
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

Note : if your optimizations is in shared file, you could desactivated it also without the "optimizations": "optimizations.json". Just remove it.

Re-generate Optimizations before each Page Response

You can ask files to be regenerated before each page response with "stylesheetsBundlesBeforeResponse": false et `"javascriptBundlesBeforeResponse": false`` for each type of Bundle.

{
    "imagesOptimizationsBeforeResponse": false,
    "optimizations": {
        "images": {
            "media/images/example.png": "media/images/optimized/",
            "media/images/example.jpg": "media/images/optimized/",
            "media/images/example.gif": "media/images/optimized/",
            "media/images/example.svg": "media/images/optimized/"
        }
    },
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

Note : this is not recommanded for production environment because it's slowed responses pages.

CSS Inline Injection for Manage Email Assets

When you create templates for sending email newsletters, or even simple message, you can not attach stylesheet. The only way is to write the CSS instructions in the template within the style markup attribute.

Specific Injection

With injectCss, simply design your template as usual via a stylesheet and NodeAtlas inject each rendering styles in the attribute style. It will do more than generate templates.

With for example the following configuration:

{
    "routes": {
        "/": {
            "template": "email.htm",
            "generate": "welcome.html",
            "injectCss": "stylesheets/email.css"
        }
    }
}

and the following set of files:

├─ generates/
├─ assets/
│  └─ stylesheets/
│     └─ email.css
├─ templates/
│  └─ email.htm
└─ webconfig.json

whose contents are :

stylesheets/common.css

body {
    color: #f00;
}

templates/email.htm*

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Email</title>
    </head>
    <body>
        <p>This is a template email.</p>
    </body>
</html>

output will be, with the command node </path/to/>node-atlas/ --generate, all following file:

├─ generates/
│  └─ bienvenue.html    <= template email generate !
├─ assets/
│  └─ stylesheets/
│     └─ email.css
├─ templates/
│  └─ email.htm
└─ webconfig.json

with as content for generates/welcome.html

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Email</title>
    </head>
    <body style="color: #f00;">
        <p>This is a template email.</p>
    </body>
</html>

This mechanism also works if you do not intend to generate anything but a site that is running. Convenient to change your live models before generating.

Test : From ./tests/examples/css-injection run node "../../../" --generate. Result are into generates.

Global Injection

It is possible to use injectCss as global mechanism for all pages.

{
    "injectCss": "stylesheets/email.css",
    "routes": {
        "/welcome/": {
            "template": "email-a.htm",
            "generate": "welcome.html"
        },
        "/good-bye/": {
            "template": "email-b.htm",
            "generate": "good-bye.html"
        }
    }
}

ainsi les deux pages welcome et good-bye contiendront chacune <body style="color: #f00;">.

Multiple Injection

It's possible to :

{
    "injectCss": ["stylesheets/reset.css", "stylesheets/email.css"],
    "routes": {
        "/welcome/": {
            "template": "email-a.htm",
            "generate": "welcome.html",
            "injectCss": "/stylesheets/welcome.css"
        },
        "/good-bye/": {
            "template": "email-b.htm",
            "generate": "good-bye.html",
            "injectCss": ["stylesheets/good-bye.css", "/stylesheets/others.css"]
        }
    }
}

Test : From ./tests/examples/css-injection run node "../../../" --generate --webconfig webconfig.multiple.json. Result are into generates.

Allow / Disallow GET / POST requests

You can also manager how the server will respond to requests GET/POST to a given page. For example, we will allow access to pages only GET for the whole site and allow a POST to one page only (and prohibited him GET).

{
    "getSupport": true,
    "postSupport": false,
    "routes": {
        "/": {
            "template": "index.htm"
        },
        "/list-of-members/": {
            "template": "members.htm"
        },
        "/write-comment/": {
            "template": "write-com.htm"
        },
        "/save-comment/": {
            "template": "save-com.htm",
            "getSupport": false,
            "postSupport": true
        }
    }
}

Note : If nothing is set, getSupport and postSupport are set to true in global webconfig and by route.

Allow / Disallow PUT / DELETE requests

Fonctionnant exactement de la même manière que getSupport et postSupport, les deux actions HTTP PUT et DELETE qui part défaut ne sont pas activé peuvent être activé avec putSupport et deleteSupport.

{
    "getSupport": false,
    "postSupport": false,
    "putSupport": true,
    "routes": {
        "/read-all-entry/": {
            "template": "display-json.htm",
            "variation": "all-entry.json",
            "getSupport": true,
            "putSupport": false
        },
        "/read-entry/:id/": {
            "template": "display-json.htm",
            "variation": "entry.json",
            "getSupport": true,
            "putSupport": false
        },
        "/create-entry/:id/": {
            "template": "display-json.htm",
            "variation": "entry.json",
            "postSupport": true,
            "putSupport": false
        },
        "/update-entry/:id/": {
            "template": "display-json.htm",
            "variation": "entry.json"
        },
        "/delete-entry/:id/": {
            "template": "display-json.htm",
            "variation": "entry.json",
            "deleteSupport": true,
            "putSupport": false
        }
    }
}

With the configuration below, only one HTTP action is possible by route, this is a great way to create APIs REST easily with NodeAtlas.

Change settings of Sessions

Key and Secret

NodeAtlas itself manages sessions stored on the server as initial settings:

that allow customers to stay connected through the pages to a single set of personal server side variable.

It is possible to change the default settings (and even compulsory for productions sites) with the parameters of webconfig.json following:

{
    sessionKey: "personal key",
    sessionSecret: "personal secret"
}

NodeAtlas also employs a memory storage object (MemoryStore) stoques that the information in the RAM of the server.

Other Parameters

It is possible to change all the parameters of the sessions (except MemoryStore) using the configuration of next webconfig.json:

{
    "session": {
        "key": "personal key",
        "secret": "personal secret",
        "cookie": {
            "path": '/',
            "httpOnly": true,
            "secure": false,
            "maxAge": null
        },
        ...,
        ...,
        ...
    }
}

The entirety of the possible configuration is located on the module documentation express-session.

External Storage Sessions

By default, this is NodeAtlas server that stores sessions in the RAM of the server application. This does not allow users to share sessions across multiple applications NodeAtlas (or other) and erases all current sessions for an application if you restart it.

To address this concern, it should support the recording sessions via a base No SQL such as Redis or MongoBD.

You just have to use the setSessions function incontrollers/common.js of Back-end part.

Session managed with Redis

Implement the following code in controllers/common.js to store your sessions in a local Redis.

var website = {};

(function (publics) {
    "use strict";

    publics.loadModules = function (NA) {
        var NA = this;

        NA.modules.RedisStore = require('connect-redis');
    };

    publics.setSessions = function (next) {
        var NA = this,
            session = NA.modules.session,
            RedisStore = NA.modules.RedisStore(session);

        NA.sessionStore = new RedisStore();

        next();
    };

}(website));

exports.loadModules = website.loadModules;
exports.setSessions = website.setSessions;

More information to connect-redis page.

Session managed with MongoDB

Implement the following code in controllers/common.js to store sessions in the database sessions of a local MongoDB.

var website = {};

(function (publics) {
    "use strict";

    publics.loadModules = function () {
        var NA = this;

        NA.modules.MongoStore = require('connect-mongo');
    };

    publics.setSessions = function (next) {
        var NA = this,
            session = NA.modules.session,
            MongoStore = NA.modules.MongoStore(session);

        NA.sessionStore = new MongoStore({
            db: 'sessions'
        });

        next();
    };

}(website));

exports.loadModules = website.loadModules;
exports.setSessions = website.setSessions;

More information to connect-redis page.

Changing the template engine brackets <% %>

For example, to include part of a file instruction is used <%- include('head.htm') %>. It would be possible to do it with <?- include('head.htm') ?> with the configuration below:

{
    "templateEngineDelimiter": "?",
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

See the exemple in files below:

components/head.htm

<!DOCTYPE html>
<html lang="fr-fr">
    <head>
        <meta charset="utf-8" />
        <title><?- specific.titlePage ?></title>

        <link type="text/css" rel="stylesheet" href="stylesheets/<?= common.classCssCommon ?>.css" media="all" />
        <link type="text/css" rel="stylesheet" href="stylesheets/<?= specific.classPage ?>.css" media="all" />
    </head>
    <body class="<?= specific.classPage ?>">

components/foot.htm

        <script async type="text/javascript" src="javascript/<?= common.classJsCommon ?>.js"></script>
    </body>
</html>

templates/template.htm

    <?- include('head.htm') ?>

    <div class="title"><?- common.titleWebsite ?></div>

    <div>
        <h1><?- specific.titlePage ?></h1>
        <?- specific.content ?>
    </div>

    <?- include('foot.htm') ?>

Learn all about the possibilities of the template engine consult the documentation ejs

Note : If nothing is set, templateEngineDelimiter is set to %.

Change the url hostname and listening port

It is possible to generate a different url listening other port with urlHostname and urlPort*. For example, the local loop listens on port 80 for a script makes the Reverse Proxy from the port 7777 on the 80 with the "http-proxy" module as below:

{
    "httpPort": 7777,
    "httpHostname": "127.0.0.1",
    "urlPort": 80,
    "urlHostname": "localhost",
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

It's also possible to avoid other enter url. Also if www.localhost or localhost:7777 are enter into url area, it's localhost for the user :

{
    "enableForceDomain": true,
    "httpPort": 7777,
    "httpHostname": "127.0.0.1",
    "urlPort": 80,
    "urlHostname": "localhost",
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

Generate urls dynamically

Relative paths in absolute

It is possible that the paths created from your url to be interpreted as subfolders that have actually no real existence. This has the effect the address media/images/example.jpg initially accessible from template displayed to address http://localhost impossible to reach when the template is displayed to address http://localhost/sub-directory/ (because the path should be ../media/images/example.jpg).

To no longer have to worry about access to resources regardless of the URL that is requested, simply turn on all the urls such as:

<link rel="stylesheet" type="text/css" href="stylesheets/common.css" />
<!-- ... -->
<img src="media/images/example.jpg" />
<!-- ... -->
<script type="text/javascript" src="javascript/common.js"></script>

in absolute urls with variable urlBasePath as below:

<link rel="stylesheet" type="text/css" href="<%= urlBasePath %>stylesheets/common.css" />
<!-- ... -->
<img src="<%= urlBasePath %>media/images/example.jpg" />
<!-- ... -->
<script type="text/javascript" src="<%= urlBasePath %>javascript/common.js"></script>

Note that in the case of the following configuration:

{
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

urlBasePath return http://localhost/ while in this configuration:

{
    "httpPort": 7777,
    "urlRelativeSubPath": "sub/folder",
    "routes": {
        "/": {
            "template": "index.htm"
        }
    }
}

urlBasePath return http://localhost:7777/sub/folder/.

The paths of templates

Using the following webconfig:

{
    "routes": {
        "/index.html": {
            "template": "index.htm"
        },
        "/contact.html": {
            "template": "contact.htm"
        }
    }
}

and the corresponding template

<!-- ... -->
<a href="http://localhost/index.html">Link to home</a>
<a href="http://localhost/contact.html">Link to contact</a>
<!-- ... -->

I'd have to change my link in the template if I change the listening port or if I change the path of the url. The following configuration changes:

{
    "httpPort": 7777,
    "routes": {
        "/home.html": {
            "template": "index.htm"
        },
        "/contact-us.html": {
            "template": "contact.htm"
        }
    }
}

me contraindrait à modifier le template précédent comme suit :

<!-- ... -->
<a href="http://localhost:7777/home.html">Link to home</a>
<a href="http://localhost:7777/contact-us.html">Link to contact</a>
<!-- ... -->

You can solve this problem by giving a key to a specific path and deporting are way in the url property.

With the followinh webconfig:

{
    "routes": {
        "index": {
            "url": "/index.html",
            "template": "index.htm"
        },
        "contact": {
            "url": "/contact.html",
            "template": "contact.htm"
        }
    }
}

I can now write the link in the dynamic template:

  1. as follows

    <!-- ... -->
    <a href="<%= urlBasePath %><%= webconfig.routes.home.url.slice(1) %>">Link to home</a>
    <a href="<%= urlBasePath %><%= webconfig.routes.contact.url.slice(1) %>">Link to contact</a>
    <!-- ... -->
    

    Note : .slice(1) makes it easy to remove the dual / for standard url.

  2. or as follows

    <!-- ... -->
    <a href="<%= urlBasePath %>.<%= webconfig.routes.home.url %>">Link to home</a>
    <a href="<%= urlBasePath %>.<%= webconfig.routes.contact.url %>">Link to contact</a>
    <!-- ... -->
    

    Note : This would, for example http://localhost/./home.html, which is a standard url.

  3. ou comme suit

    <!-- ... -->
    <a href="<%= urlBasePathSlice + webconfig.routes.home.url %>">Link to home</a>
    <a href="<%= urlBasePathSlice + webconfig.routes.contact.url %>">Link to contact</a>
    <!-- ... -->
    

    Note : urlBasePathSlice return http://localhost in place of http://localhost/ or http://localhost:7777/sub/folder in place of http://localhost:7777/sub/folder/.

Utilisation de la clé pour mapper les pages

It's maybe useful to know the key used for the current page displayed for find the equivalent page in an other language.

With the following webconfig :

{
    "languageCode": "en-us",
    "routes": {
        "index_en-us": {
            "url": "/",
            "template": "/index.htm"
        },
        "index_fr-fr": {
            "url": "/francais/",
            "template": "index.htm",
            "languageCode": "fr-fr"
        },
        "cv_en-us": {
            "url": "/resume/",
            "template": "cv.htm"
        },
        "cv_fr-fr": {
            "url": "/francais/cv/",
            "template": "index.htm",
            "languageCode": "fr-fr"
        }
    }
}

and the common variation following :

{
    "language": [{
        "name": "English",
        "code": "en-us"
    }, {
        "name": "French",
        "code": "fr-fr"
    }]
}

in fr :

{
    "language": [{
        "name": "Anglais",
        "code": "en-us"
    }, {
        "name": "Français",
        "code": "fr-fr"
    }]
}

we could create link between each page as following :

<ul>
    <% for (var i = 0; i < common.language.length; i++) { %>
    <li><a href="<%= urlBasePathSlice + webconfig.routes[currentRouteName.split('_')[0] + '_' + common.language[i].code].url %>"><%- common.language[i].name %></a></li>
    <% } %>
</ul>