Minimalistic Web Application Framework as Promised
BlueGate is a simple framework to build web applications in NodeJS. It is build on top of the powerful Bluebird library to let you use the ease of Promises to its fullest extent.
Instead of a simple stack with middleware, BlueGate has a sophisticated request flow that fits both REST API's and complex multi-tier applications.
BlueGate can be extended with the following modules:
- class to use ES6 classes to write routes.
- csrf for CSRF protection.
- handlebars for template rendering.
- session for session support.
- static to serve static assets.
Installation
Install using npm install bluegate
Quick example
var BlueGate = ; var app = ;app; // Port or unix socket. app;app; app
See the Todo example for a more complete example.
Request flow
Each request follows the steps below:
initialize
can be used to register request specific handlersauthentication
should be used to identify the clientauthorisation
should be used for permission checksprevalidation
does validation before preprocessingpreprocess
does the preprocessing (e.g. parsing body)postvalidation
does validation after preprocessingprocess
will generate the outputpostprocess
can alter the output (e.g. for templating)- send response to client
after
is for additional work (e.g. statistics)
All remaining steps are skipped when an error occur before sending the response, In that case, we will arrive at the error-flow:
error
is used to generate the error response for the client- send response to client
aftererror
is for additional work (e.g. statistics)
The name of each step is used as function name to register handlers for it.
This can be done on the BlueGate instance (as shown in the example above) or
on the this
scope within a handler. The first argument is in the form
METHOD /path
and determines which requests it can handle. This argument
can be omitted to enable the handler for all requests.
Writing handlers
Input
Handler functions can accept input via both function arguments and the local
scope (this
). The local scope is only accessible when using ES5 functions.
For ES6 functions you may add the request
parameter. The following
examples are identical:
// ES5app;// ES6app;
Input from path parameters is mapped to function arguments. Function arguments
that do not have a parameter will get undefined
as value.
app;
Other input is available in the local scope (this.*
) or request parameter
(request.*
).
The table below lists all available variables and functions.
Name | Type | Example | Read only? |
---|---|---|---|
host | string | www.example.com | yes |
path | string | /user/john | yes |
method | string | GET | yes |
body | * | yes | |
mime | string | text/html | no |
status | int | 200 | no |
query | object | ['page'] | yes |
headers | object | {'User-Agent': '...'} | yes |
cookies | object | ['sessionId'] | yes |
ip | string | 127.0.0.1 | yes |
date | date | yes | |
secure | bool | false | yes |
parameters | object | {...} | yes |
multipartBoundary | string | yes |
The body type is dependent on the Content-Type header sent by the client.
Content-Type | Type |
---|---|
application/json | object |
application/form-data | object |
text/* | buffer |
/ | Readable stream |
The multipartBoundary
property is only set when the Content-Type header was set to multipart/*
.
Parsing multipart data is not done by this framework, since the application may stream the input directly to
files or external storage, which is beyond the scope of BlueGate. Popular modules for parsing are
busboy,
multiparty and
dicer.
See the upload example
for more information on how to handle multipart/form-data
requests and file uploads.
Path parameters
Path parameters are passed as function arguments, as shown in the last code example. The following types are available:
Type | Description |
---|---|
alpha | Alpha characters (a-z A-Z) |
alphanum | Alphanumeric characters (a-z A-Z 0-9) |
bool | Boolean (matches "1", "0", "true" and "false") |
float | Floats (e.g. -34.3, .3 or 63) |
int | Positive integer (1..n). Does not match 0. |
path | Matches all characters including forward slashes ("/") |
signed | Signed integer (e.g. -123, 0 or 123) |
string | Matches all characters except forward slashes ("/") |
unsigned | Unsigned integer (0..n) |
uuid | Matches UUID versions 1 to 5 |
Accepting path parameters via function arguments should be preferred above using
this.parameters
. The last object was added to allow abstract functions to
handle multiple callbacks.
It is possible to set new parameters or override existing using setParameter
inside a handler. This is illustrated in the following example:
app;app;
The setParameter
function requires two arguments; name and value. The value
is not casted to the type defined in the path.
Query arguments
Values from the path query ("/news?page=34") can be retreived using
this.getQuery
. The first argument is the name and the second is the value
type. A default value can be provided as thirth argument (defaults to null
).
The default value is returned when the variable is missing or its value does not
match the requested type.
app;
An array of all available query variables is available in this.query
. This
contains a list of names only, to enforce validation when getting the value.
Output
Output is provided as return value. This can be provided as strings, buffer, readable stream or any JSON serializable value. The MIME-type defaults to "text/html" when using strings, "application/octet-stream" for buffers and stream and "application/json" for other types. JSON output is automatically encoded.
Use this.mime
to set a different MIME-type.
app;
Cookies
Read cookies using getCookie
. This is similar to getQuery
. The names of
all provided cookies can be found in this.cookies
.
app;
Use the setCookie
function to set a cookie. Arguments are:
- Name May not contain whitespace, comma, semicolon, equals sign or non-printable characters.
- Value May not contain whitespace, comma, semicolon or non-printable characters.
- Expires
Given as JavaScript
Date
-object. Optional, defaults to not expiration date (session cookie). - Path E.g. "/forum". Optional.
- Domain E.g. ".example.com". Optional.
- HttpOnly
Set HttpOnly flag. Given as boolean. Optional, defaults to
true
. - Secure
Set Secure flag. Given as boolean. Optional, defaults to
true
when visited over SSL.
Example:
app;
HTTP headers
HTTP headers can be set using the setHeader
function.
app;
An optional thirth argument can be provided to append headers instead of replacing them.
app;
HTTP status code
The HTTP status code is 200 by default. This code is changed automatically when an error occurs. The HTTP status for errors is dependent on the phase in which the error occurred.
Phase | Code | Message |
---|---|---|
initialize |
500 | Internal server error |
authentication |
401 | Authentication required |
authorisation |
403 | Permission denied |
prevalidation |
400 | Bad request |
preprocess |
500 | Internal server error |
postvalidation |
400 | Bad request |
process |
500 | Internal server error |
postprocess |
500 | Internal server error |
Additionally, a 404
response ("Not found") is provided when no process
handler was found. All phases before process
are still executed, because
it is possible that those will register a process
handler.
It is possible to override the status code from within a handler using
this.status
.
app
Logging
BlueGate will log requests to console by default. You can change this behaviour in the constructor options.
var server = log: false // or: { ... };
The format of the log messages is:
2015-05-26T21:15:05 127.0.0.1 "GET /host-test" 200 16 143
\- Start of request | | | | |
\ Client IP | | | |
\ Request | | |
Response code / | |
Response size (bytes) / |
Response time (ms) /
No settings are available to change this format. You can disable logging and
register an after
/ aftererror
handler for custom logging.
Security
Running behind a proxy server
When you are running behind a proxy server, you should set the
trustedProxies
option. This contains a list of IP-addresses used by your
proxy servers. The default value for this list is 127.0.0.1
. All proxies
must add the IP-address to the X-Forwarded-For
header. The this.ip
variable used in handlers will contain the client IP, even when the client tries
to spoof the IP-address by sending a false X-Forwarded-For
header.
var app = trustedProxies: '192.168.1.10' '192.168.1.11';
Running behind SSL
The this.secure
variable in handles indicates if the client is using HTTPS
for this request. This flag relies on the X-Forwarded-Proto
header, which
should be set by reverse proxies (which may require extra configuration).
This value can be spoofed by the client.
Cookies are set with the Secure flag by default when running behind SSL. It's
possible to remove it by setting the 7th argument of setCookie
to false
.
Clickjacking
All HTML responses will include the HTTP-header X-Frame-Options: deny
to
prevent clickjacking attacks.
It is set to the most strict setting by default. You can change its setting in
the constructor when you use iframes. Strings are used as header value. Use
false
to completely remove this header.
var app = clickjacking: 'sameorigin';
MIME-sniffing
All responses will include the header X-Content-Type-Options: nosniff
by
default. This helps to prevent
MIME-sniffing attacks. You
should leave this header in and make sure that the MIME-type is set correctly.
However, you can disable this feature in the constructor.
var app = noMimeSniffing: false;
Maximum input size
A maxInputSize
option is available. This limits the number of bytes
accepted in the request body. The default value is 1048576 (1MB). You should
lower this value if large requests aren't used to avoid DoS attacks.
var app = maxInputSize: 1024 * 64 // 64 KB;
The maximum input size does not apply to posted streams (e.g. multipart data). A process function can stop the upload by sending a response, ignoring the stream.