njs API

Core
     String
     JSON
     Crypto
HTTP
     Request
Stream
     Session
Examples
     URL Decoding
     URL Encoding
     Internal Redirect
     Returning Fastest Response from Proxy
     Creating HS JWT
     Accessing API from a Subrequest
     Creating secure_link Hash

njs provides objects, methods and properties for extending nginx functionality.

Core

String

There are two types of strings: a Unicode string (default) and a byte string.

A Unicode string corresponds to an ECMAScript string which contains Unicode characters.

Byte strings contain a sequence of bytes. They are used to serialize Unicode strings to external data and deserialize from external sources. For example, the toUTF8() method serializes a Unicode string to a byte string using UTF8 encoding:

>> '£'.toUTF8().toString('hex')
c2a3  /* C2 A3 is the UTF8 representation of 00A3 ('£') code point */

The toBytes() method serializes a Unicode string with code points up to 255 into a byte string, otherwise, null is returned:

>> '£'.toBytes().toString('hex')
a3  /* a3 is a byte equal to 00A3 ('£') code point  */

Only byte strings can be converted to different encodings. For example, a string cannot be encoded to hex directly:

>> 'αβγδ'.toString('base64')
TypeError: argument must be a byte string
    at String.prototype.toString (native)
    at main (native)

To convert a Unicode string to hex, first, it should be converted to a byte string and then to hex:

>> 'αβγδ'.toUTF8().toString('base64')
zrHOss6zzrQ=

String.fromCodePoint(codePoint1[, ...[, codePoint2]])
Returns a string from one or more Unicode code points.
>> String.fromCodePoint(97, 98, 99, 100)
abcd
String.prototype.concat(string1[, ..., stringN])
Returns a string that contains the concatenation of specified strings.
>> "a".concat("b", "c")
abc
String.prototype.endsWith(searchString[, length])
Returns true if a string ends with the characters of a specified string, otherwise false. The optional length parameter is the the length of string. If omitted, the default value is the length of the string.
>> 'abc'.endsWith('abc')
true
>> 'abca'.endsWith('abc')
false
String.prototype.fromBytes(start[, end])
(njs specific) Returns a new Unicode string from a byte string where each byte is replaced with a corresponding Unicode code point.
String.prototype.fromUTF8(start[, end])
(njs specific) Converts a byte string containing a valid UTF8 string into a Unicode string, otherwise null is returned.
String.prototype.includes(searchString[, position]))
Returns true if a string is found within another string, otherwise false. The optional position parameter is the position within the string at which to begin search for searchString. Default value is 0.
>> 'abc'.includes('bc')
true
String.prototype.indexOf(searchString[, fromIndex])
Returns the position of the first occurrence of the searchString The search is started at fromIndex. Returns -1 if the value is not found. The fromIndex is an integer, default value is 0. If fromIndex is lower than 0 or greater than String.prototype.length, the search starts at index 0 and String.prototype.length.
>> 'abcdef'.indexOf('de', 2)
3
String.prototype.lastIndexOf(searchString[, fromIndex])
Returns the position of the last occurrence of the searchString, searching backwards from fromIndex. Returns -1 if the value is not found. If searchString is empty, then fromIndex is returned.
>> "nginx".lastIndexOf("gi")
1
String.prototype.length
Returns the length of the string.
>> 'αβγδ'.length
4
String.prototype.match([regexp])
Matches a string against a regexp.
>> 'nginx'.match( /ng/i )
ng
String.prototype.repeat(number)
Returns a string with the specified number of copies of the string.
>> 'abc'.repeat(3)
abcabcabc
String.prototype.replace([regexp|string[, string|function]])
Returns a new string with matches of a pattern (string or a regexp) replaced by a string or a function.
>> 'abcdefgh'.replace('d', 1)
abc1efgh
String.prototype.search([regexp])
Searches for a string using a regexp
>> 'abcdefgh'.search('def')
3
String.prototype.slice(start[, end])
Returns a new string containing a part of an original string between start and end or from start to the end of the string.
>> 'abcdefghijklmno'.slice(NaN, 5)
abcde
String.prototype.startsWith(searchString[, position])
Returns true if a string begins with the characters of a specified string, otherwise false. The optional position parameter is the position in this string at which to begin search for searchString. Default value is 0.
>> 'abc'.startsWith('abc')
true
> 'aabc'.startsWith('abc')
false
String.prototype.substr(start[, length])
Returns the part of the string of the specified length from start or from start to the end of the string.
>>  'abcdefghijklmno'.substr(3, 5)
defgh
String.prototype.substring(start[, end])
Returns the part of the string between start and end or from start to the end of the string.
>> 'abcdefghijklmno'.substring(3, 5)
de
String.prototype.toBytes(start[, end])
(njs specific) Serializes a Unicode string to a byte string. Returns null if a character larger than 255 is found in the string.
String.prototype.toLowerCase()
Converts a string to lower case. The method supports only simple Unicode folding.
>> 'ΑΒΓΔ'.toLowerCase()
αβγδ
String.prototype.toString([encoding])

If no encoding is specified, returns a specified Unicode string or byte string as in ECMAScript.

(njs specific) If encoding is specified, encodes a byte string to hex, base64, or base64url.

>>  'αβγδ'.toUTF8().toString('base64url')
zrHOss6zzrQ
String.prototype.toUpperCase()
Converts a string to upper case. The method supports only simple Unicode folding.
>> 'αβγδ'.toUpperCase()
ΑΒΓΔ
String.prototype.toUTF8(start[, end])
(njs specific) Serializes a Unicode string to a byte string using UTF8 encoding.
>> 'αβγδ'.toUTF8().length
8
>> 'αβγδ'.length
4
String.prototype.trim()
Removes whitespaces from both ends of a string.
>> '   abc  '.trim()
abc
String.prototype.split(([string|regexp[, limit]]))
Returns match of a string against a regexp. The optional limit parameter is an integer that specifies a limit on the number of splits to be found.
>> 'abc'.split('')
a,b,c
encodeURI(URI)
encodes a URI by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character
>> encodeURI('012αβγδ')
012%CE%B1%CE%B2%CE%B3%CE%B4
encodeURIComponent(encodedURIString)
Encodes a URI by replacing each instance of certain characters by one, two, three, or four escape sequences representing the UTF-8 encoding of the character.
>> encodeURIComponent('[@?=')
%5B%40%3F%3D
decodeURI(encodedURI)
Decodes a previously encoded URI.
>> decodeURI('012%CE%B1%CE%B2%CE%B3%CE%B4')
012αβγδ
decodeURIComponent(decodedURIString)
Decodes an encoded component of a previously encoded URI.
>> decodeURIComponent('%5B%40%3F%3D')
[@?=

JSON

The JSON object (ES 5.1) provides functions to convert njs values to and from JSON format.

JSON.parse(string[, reviver])
Converts a string that represents JSON data into an njs object ({...}) or array ([...]). The optional reviver parameter is a function (key, value) that will be called for each (key,value) pair and can transform the value.
JSON.stringify(value[, replacer] [, space])
Converts an njs object back to JSON. The obligatory value parameter is generally a JSON object or array that will be converted. If the value has a toJSON() method, it defines how the object will be serialized. The optional replacer parameter is a function or array that transforms results. The optional space parameter is a string or number. If it is a number, it indicates the number of white spaces placed before a result (no more than 10). If it is a string, it is used as a white space (or first 10 characters of it). If omitted or is null, no white space is used.

>> var json = JSON.parse('{"a":1, "b":true}')
>> json.a
1

>> JSON.stringify(json)
{"a":1,"b":true}

>> JSON.stringify(json, undefined, 1)
{
"a": 1,
"b": true
}

>> JSON.stringify({ x: [10, undefined, function(){}] })
{"x":[10,null,null]}

>> JSON.stringify({"a":1, "toJSON": function() {return "xxx"}})
"xxx"

# Example with function replacer

>> function replacer(key, value) {return (typeof value === 'string') ? undefined : value}
>>JSON.stringify({a:1, b:"b", c:true}, replacer)
{"a":1,"c":true}

Crypto

The Crypto module provides cryptographic functionality support. The Crypto module object is returned by require('crypto').

crypto.createHash(algorithm)
Creates and returns a Hash object that can be used to generate hash digests using the given algorithm. The algorighm can be md5, sha1, and sha256.
crypto.createHmac(algorithm, secret key)
Creates and returns an HMAC object that uses the given algorithm and secret key. The algorighm can be md5, sha1, and sha256.

Hash

hash.update(data)
Updates the hash content with the given data.
hash.digest([encoding])
Calculates the digest of all of the data passed using hash.update(). The encoding can be hex, base64, and base64url. If encoding is not provided, a byte string is returned.

>> var cr = require('crypto')
undefined

>> cr.createHash('sha1').update('A').update('B').digest('base64url')
BtlFlCqiamG-GMPiK_GbvKjdK10

HMAC

hmac.update(data)
Updates the HMAC content with the given data.
hmac.digest([encoding])
Calculates the HMAC digest of all of the data passed using hmac.update(). The encoding can be hex, base64, and base64url. If encoding is not provided, a byte string is returned.

>> var cr = require('crypto')
undefined

>> cr.createHmac('sha1', 'secret.key').update('AB').digest('base64url')
Oglm93xn23_MkiaEq_e9u8zk374

HTTP

The HTTP object is available only in the ngx_http_js_module module. All string properties of the HTTP object are byte strings.

Request

r.args{}
request arguments object, read-only
r.error(string)
writes a string to the error log on the error level of logging
r.finish()
finishes sending a response to the client
r.headersIn{}
incoming headers object, read-only.

For example, the Header-Name header can be accessed with the syntax headers['Header-Name'] or headers.Header_name

r.headersOut{}
outgoing headers object, writable.

For example, the Header-Name header can be accessed with the syntax headers['Header-Name'] or headers.Header_name

r.httpVersion
HTTP version, read-only
r.log(string)
writes a string to the error log on the info level of logging
r.internalRedirect(uri)
performs an internal redirect to the specified uri. If the uri starts with the “@” prefix, it is considered a named location.
r.method
HTTP method, read-only
r.parent
references the parent request object
r.remoteAddress
client address, read-only
r.requestBody
holds the request body, read-only
r.responseBody
holds the subrequest response body, read-only
r.return(status[, string])
sends the entire response with the specified status to the client

It is possible to specify either a redirect URL (for codes 301, 302, 303, 307, and 308) or the response body text (for other codes) as the second argument

r.send(string)
sends a part of the response body to the client
r.sendHeader()
sends the HTTP headers to the client
r.status
status, writable
r.variables{}
nginx variables object, read-only
r.warn(string)
writes a string to the error log on the warning level of logging
r.uri
current URI, read-only
r.subrequest(uri[, options[, callback]])
creates a subrequest with the given uri and options, and installs an optional completion callback.

If options is a string, then it holds the subrequest arguments string. Otherwise, options is expected to be an object with the following keys:

args
arguments string
body
request body
method
HTTP method

The completion callback receives a subrequest response object with methods and properties identical to the parent request object.

Stream

The stream object is available only in the ngx_stream_js_module module. All string properties of the stream object are byte strings.

Session

s.remoteAddress
client address, read-only
s.eof
a boolean read-only property, true if the current buffer is the last buffer
s.fromUpstream
a boolean read-only property, true if the current buffer is from the upstream server to the client
s.buffer
the current buffer, writable
s.variables{}
nginx variables object, read-only
s.OK
the OK return code
s.DECLINED
the DECLINED return code
s.AGAIN
the AGAIN return code
s.ERROR
the ERROR return code
s.ABORT
the ABORT return code
s.log(string)
writes a sent string to the error log on the info level of logging
s.warn(string)
writes a sent string to the error log on the warning level of logging
s.error(string)
writes a sent string to the error log on the error level of logging

Examples

URL Decoding

js_include urldecode.js;

js_set $decoded_foo decoded_foo;

The urldecode.js file:

function decoded_foo(r) {
    return decodeURIComponent(r.args.foo);
}

URL Encoding

js_include urlencode.js;

js_set $encoded_foo encoded_foo;
...

location / {
    proxy_pass http://example.com?foo=$encoded_foo;
}

The urlencode.js file:

function encoded_foo(r) {
    return encodeURIComponent('foo & bar?');
}

Internal Redirect

js_include redirect.js;

location /redirect {
    js_content redirect;
}

@named {
    return 200 named;
}

The redirect.js file:

function redirect(r) {
    r.internalRedirect('@named');
}

Returning Fastest Response from Proxy

js_include fastresponse.js;

location /start {
    js_content content;
}

location /foo {
    proxy_pass http://backend1;
}

location /bar {
    proxy_pass http://backend2;
}

The fastresponse.js file:

function content(r) {
    var n = 0;

    function done(res) {
        if (n++ == 0) {
            r.return(res.status, res.responseBody);
        }
    }

    r.subrequest('/foo', r.variables.args, done);
    r.subrequest('/bar', r.variables.args, done);
}

Creating HS JWT

js_include hs_jwt.js;

js_set $jwt jwt;

The hs_jwt.js file:

function create_hs256_jwt(claims, key, valid) {
    var header = { "typ" : "JWT", "alg" : "HS256", "exp" : Date.now() + valid };

    var s = JSON.stringify(header).toBytes().toString('base64url') + '.'
            + JSON.stringify(claims).toBytes().toString('base64url');

    var h = require('crypto').createHmac('sha256', key);

    return s + '.' + h.update(s).digest().toString('base64url');
}

function jwt(r) {
    var claims = {
        "iss" : "nginx",
        "sub" : "alice",
        "foo" : 123,
        "bar" : "qq",
        "zyx" : false
    };

    return create_hs256_jwt(claims, 'foo', 600);
}

Accessing API from a Subrequest

js_include subrequest.js;

keyval_zone zone=foo:10m;
...

location /keyval {
    js_content set_keyval;
}

location /version {
    js_content version;
}

location /api {
    api write=on;
}

The subrequest.js file:

function set_keyval(r) {
    r.subrequest('/api/3/http/keyvals/foo',
        { method: 'POST',
          body: JSON.stringify({ foo: 789, bar: "ss dd 00" })},

        function(res) {
            if (res.status >= 300) {
                r.return(res.status, res.responseBody);
                return;
            }
            r.return(500);
        });
}

function version(r) {
    r.subrequest('/api/3/nginx', { method: 'GET' }, function(res) {
        if (res.status != 200) {
            r.return(res.status);
            return;
        }

        var json = JSON.parse(res.responseBody);
        r.return(200, json.version);
    });
}

Creating secure_link Hash

js_include hash.js;

js_set $new_foo create_secure_link;
...

location / {
    secure_link $cookie_foo;
    secure_link_md5 "$uri mykey";
    ...
}

location @login {
    add_header Set-Cookie "foo=$new_foo; Max-Age=60";
    return 302 /;
}

The hash.js file:

function create_secure_link(r) {
    return require('crypto').createHash('md5')
                            .update(r.uri).update(" mykey")
                            .digest('base64url');
}