Security News
Input Validation Vulnerabilities Dominate MITRE's 2024 CWE Top 25 List
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
citizen is an event-driven MVC framework for Node.js web applications. It's still in a pre-alpha state and not suitable for public consumption, but I wanted to get it out there before the name was taken.
The goal of citizen is to handle serving, routing, and event emitter creation, while providing some useful helpers to get you on your way. The nuts and bolts of your application are up to you, but citizen's helpers are designed to work with certain patterns and conventions, which are covered throughout this guide.
The only dependency at this point is Handlebars. Current static file serving is just a hack to enable development; I use nginx as a front end and I'm debating whether I should even add a module to incorporate file serving into citizen.
npm install citizen
I had some issues because of the Handlebars dependency, but installing with the --no-bin-links
flag worked:
npm install citizen --no-bin-links
citizen can accept arguments when it starts, so initializing it is a bit different from typical Node.js modules because it's a function call. The following assignment will initialize citizen with the default configuration.
app = require('citizen')();
You can pass arguments to change citizen's startup parameters:
app = require('citizen')({
// Mode determines certain framework behaviors such as error handling (dumps vs. friendly errors).
// Options are 'debug', 'development', or 'production'. Default is 'production'.
mode: 'debug',
// Full directory path pointing to this app. Default is '/'.
appPath: '/path/to/your/app',
// Path to your MVC patterns
patternPath: '/path/to/your/patterns',
// Full directory path pointing to your web root (necessary if citizen will be serving up your static
// files as well, but not recommended). Default is '/'.
webRoot: '/srv/www/myapp/static',
// If the full web address is 'http://www.mysite.com/to/myapp', then this setting would be '/to/myapp'.
// Default is '/'.
appUrlFolder: '/to/myapp',
// Port for the web server. Default is 80.
httpPort: 8080,
// Enable session management
sessions: true,
// Session length in milliseconds. Default is 1200000 (20 minutes).
sessionLength: 600000
});
The only objects citizen returns are its configuration (app.config
) and helper functions (app.helper
). It also creates a global namespace called CTZN
that it uses for session storage and other things. You should avoid accessing this namespace directly; anything that you might use in your application will be exposed by the server through local scopes.
app.server.start();
Apps using citizen have a simple URL structure that determines which controller to fire, passes URL parameters, and makes a bit of room for SEO-friendly content. The structure looks like this:
http://www.site.com/pattern-name/SEO-content-goes-here/parameterName/value/anotherParameter/anotherValue
For example, let's say your site's base URL is:
http://www.cleverna.me/
Requesting that URL will cause the index
controller to fire, because the index pattern is the default pattern. The following URL will also cause the index controller to fire:
http://www.cleverna.me/index
If you have an article
pattern, you'd request it like this:
http://www.cleverna.me/article
Instead of query strings, citizen uses an SEO-friendly method of passing URL parameters consisting of name/value pairs. If you had to pass an article ID of 237 to get the correct article and a page number of 2 to get the correct page, you'd append name/value pairs to the URL:
http://www.cleverna.me/article/id/237/page/2
citizen also lets you optionally insert relevent content into your URLs, like so:
http://www.cleverna.me/article/My-clever-article-title/id/237/page/2
This content is not parsed by the framework in any way and can be whatever you like, but it must always immediately follow the pattern name and precede any name/value pairs.
citizen relies on a predefined model-view-controller pattern that has a few strict requirements. As discussed above, the following URL will cause the article
pattern to fire:
http://www.cleverna.me/article/My-clever-article-title/id/237/page/2
The article pattern requires the following structure:
/your-app-path/patterns/article/article-controller.js
/your-app-path/patterns/article/article-model.js
/your-app-path/patterns/article/article.html
Each controller requires at least one public function named handler()
. The citizen server calls handler()
after it processes the initial request and passes it two arguments: an object containing the parameters of the request and an emitter for the controller to emit when it's done.
// article-controller.js
exports.handler = handler;
function handler(args, emitter) {
// Do some stuff, and when it's ready to go, emit the 'ready' event and pass the args object back to the server
emitter.emit('ready', args);
};
When it's first passed from the server, args
contains the following objects:
request
: The inital request object received by the server
response
: The response object sent by the server
route
: Details of the route, such as the requested URL and the name of the route (controller)
url
: Any URL parameters that were passed (See "Routing and URLs" above)
content
: An empty object where you can place content that will be delivered to the view
form
: Data collected from a POST, if available
cookie
: An object containing any cookies that were sent with the request
session
: An object containing any session variables
Based on the example URL above, you'll have the following url
object:
{
id: 237,
page: 2
}
You'll notice that args
gets passed back to the server, which has two purposes. First, anything you want to put into your view should be appended to args.content
. For example:
// article-controller.js
var model = require('./article-model.js');
exports.handler = handler;
function handler(args, emitter) {
args.content = model.getContent(args.url.id, args.url.page);
emitter.emit('ready', args);
};
Here's a simple model:
// article-model.js
exports.getContent = getContent;
function getContent(id, page) {
var articles = {
'236': {
title: 'I Hate Node.js',
summary: 'A list of things I hate about Node',
pages: {
'1': 'First page content',
'2': 'Second page content'
}
},
'237': {
title: 'I <3 Node.js',
summary: 'A list of things I love about Node',
pages: {
'1': 'First page content',
'2': 'Second page content'
}
}
};
return {
title: articles[id]['title'],
summary: articles[id]['summary'],
text: articles[id]['pages'][page]
};
};
In article.html
, you can now reference the content
object like so:
<!DOCTYPE html>
<html>
<body>
<h1>
{{content.title}}
</h1>
<p id="summary">
{{content.summary}}
</p>
<div id="text">
{{content.text}}
</div>
</body>
</html>
The other reason args
gets passed back to the server is so that you can set cookies and session variables, which are discussed next.
You set cookies by appending them to args.set.cookie
. You can set one at a time or create a complete cookie object. The following code tells the server to set username
and nickname
cookies that never expire:
// article-controller.js
function handler(args, emitter) {
args.set.cookie = {
username: {
value: 'Danny',
expires: 'never' // Valid options are 'now' (deletes an existing cookie), 'never' (current time plus 30 years),
// 'session', or time in milliseconds. Default is 'session'.
},
nickname: {
value: 'Doc',
expires: 'never'
}
};
emitter.emit('ready', args);
};
The following code sets the same cookies, but they expire at the end of the browser session:
args.set.cookie.username = 'Danny';
args.set.cookie.nickname = 'Doc';
Other cookie options include path
(default is /
), httpOnly
(default is true
), and secure
(default is false
).
Cookie variables aren't available immediately after you set them. citizen has to receive the output from the controller before it can send the cookie to the user agent, so use a local instance of the variable if you need access to it during the same request.
If sessions are enabled, citizen creates an array called CTZN.sessions
and stores its session information there. You should avoid accessing this array directly and use args.session
instead, which automatically references the current user's session.
By default, the session has one property: args.session.id
. This property is also sent to the browser as a cookie called CTZN_sessionID
.
Setting session variables is similar to setting cookie variables. Just use args.set.session
:
args.session.username = 'Danny';
Just like cookies, session variables aren't available during the same request, so use a local instance if you need to access this data right away.
If you set mode: 'debug'
at startup, citizen dumps the current pattern's output to the console by default. You can also dump it to the view with the dump
URL parameter:
http://www.cleverna.me/article/id/237/page/2/dump/view
You can specify the exact object to dump with the debug
URL parameter:
http://www.cleverna.me/article/id/237/page/2/debug/CTZN.session // Dumps CTZN.session to the console
http://www.cleverna.me/article/id/237/page/2/debug/CTZN.session/dump/view // Dumps CTZN.session to the browser
In development
mode, you must specify the debug
parameter to enable debugging. If you're in production
mode, debugging is disabled.
FAQs
Node.js MVC web application framework. Includes routing, serving, caching, session management, and other helpful tools.
The npm package citizen receives a total of 17 weekly downloads. As such, citizen popularity was classified as not popular.
We found that citizen demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 0 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
MITRE's 2024 CWE Top 25 highlights critical software vulnerabilities like XSS, SQL Injection, and CSRF, reflecting shifts due to a refined ranking methodology.
Security News
In this segment of the Risky Business podcast, Feross Aboukhadijeh and Patrick Gray discuss the challenges of tracking malware discovered in open source softare.
Research
Security News
A threat actor's playbook for exploiting the npm ecosystem was exposed on the dark web, detailing how to build a blockchain-powered botnet.