<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Compatibility</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="../../../css/style_en.css">

Planning your ingress-nginx migration? Start here:
kubernetes.nginx.org.

<input class="side-menu" type="checkbox" id="side-menu">

english

русский

news

about

download

security

documentation

faq

books

community

enterprise

community forum (new)

x.com

blog

njs

ingress controller

gateway fabric

Compatibility

njs is created in compliance with
ECMAScript 5.1
(strict mode) with some
ECMAScript 6
and later extensions.
The compliance is still evolving.

Definitions of njs specific properties and methods
not compliant with ECMAScript can be found in
Reference.
Definitions of njs properties and methods
compliant with ECMAScript can be found in
ECMAScript
specification.

What is currently supported

Boolean values, numbers, strings, objects, arrays,
functions, function constructors
(0.3.6),
and regular expressions

ES5.1 operators, ES7 exponentiation operators,
ES2020 optional chaining
(0.9.6),
ES2021 logical assignment operators
||=, &&=,
??=
(0.9.6)

ES5.1 statements:
break,
catch,
continue,
do while,
else,
finally,
for,
for in,
if,
return,
switch,
throw,
try,
var,
while,
labeled statements (0.2.8)

ES6 statements:
let (0.6.0),
const (0.6.0),
async (0.7.0),
await (0.7.0)

Math properties:

ES6:
E,
LN10,
LN2,
LOG10E,
LOG2E,
PI,
SQRT1_2,
SQRT2

Math methods:

ES6:
abs,
acos,
acosh,
asin,
asinh,
atan,
atan2,
atanh,
cbrt,
ceil,
clz32,
cos,
cosh,
exp,
expm1,
floor,
fround,
hypot,
imul,
log,
log10,
log1p,
log2,
max,
min,
pow,
random,
round,
sign,
sin,
sinh,
sqrt,
tan,
tanh,
trunc

Number properties:

ES6:
EPSILON,
MAX_SAFE_INTEGER,
MAX_VALUE,
MIN_SAFE_INTEGER,
MIN_VALUE,
NEGATIVE_INFINITY,
NaN,
POSITIVE_INFINITY

Number methods:

ES6:
isFinite,
isInteger,
isNaN,
isSafeInteger,
parseFloat,
parseInt

Number prototype methods:

ES6:
toExponential
(0.3.6),
toFixed
(0.3.6),
toPrecision
(0.3.6)

String methods:

ES5.1:
fromCharCode

ES6:
fromCodePoint

String prototype methods:

ES5.1:
charAt,
concat,
indexOf,
lastIndexOf,
match,
replace,
search,
slice,
split,
substr,
substring,
toLowerCase,
trim,
toUpperCase

ES6:
codePointAt,
endsWith,
includes,
repeat,
startsWith

ES8:
padEnd,
padStart

ES9:
trimEnd
(0.3.4),
trimStart
(0.3.4)

ES12:
replaceAll
(0.7.10)

Object methods:

ES5.1:
create (support without properties list),
defineProperties (accessor descriptors support
since 0.3.3),
defineProperty (accessor descriptors support
since 0.3.3),
freeze,
getOwnPropertyDescriptor,
getOwnPropertyDescriptors
(0.3.1),
getOwnPropertyNames
(0.3.1),
getPrototypeOf,
isExtensible,
isFrozen,
isSealed,
keys,
preventExtensions,
seal

ES6:
assign
(0.3.7)

ES8:
entries
(0.2.7),
values
(0.2.7)

Object prototype methods:

ES5.1:
hasOwnProperty,
isPrototypeOf,
(0.3.0),
propertyIsEnumerable,
toString,
valueOf

ES6:
is
(0.3.8),
setPrototypeOf
(0.3.8)

Array methods:

ES5.1:
isArray

ES6:
of

ES13:
from
(0.8.0),

Array prototype methods:

ES5.1:
concat,
every,
filter,
forEach,
indexOf,
join,
lastIndexOf,
map,
pop,
push,
reduce,
reduceRight,
reverse,
shift,
slice,
some,
sort,
splice,
unshift

ES6:
copyWithin
(0.3.7),
fill,
find,
findIndex

ES7: includes

ES13:
toReversed
(0.8.0),
toSorted
(0.8.0),
toSpliced
(0.8.0)

ArrayBuffer methods
(0.3.8):

ES6:
isView

ArrayBuffer prototype methods
(0.3.8):

ES6:
slice

Typed-array constructors
(0.3.8):

ES6:
Int8Array,
Uint8Array,
Uint8ClampedArray,
Int16Array,
Uint16Array,
Int32Array,
Uint32Array,
Float32Array,
Float64Array

Typed-array prototype methods
(0.3.8):

ES6:
copyWithin,
every
(0.4.4),
fill,
filter
(0.4.4),
find
(0.4.4),
findIndex
(0.4.4),
forEach
(0.4.4),
includes
(0.4.4),
indexOf
(0.4.4),
join,
lastIndexOf
(0.4.4),
map
(0.4.4),
reduce
(0.4.4),
reduceRight
(0.4.4),
reverse
(0.4.4),
set,
slice,
some
(0.4.4),
sort
(0.4.2),
subarray,
toString

ES13:
toReversed
(0.8.0)
toSorted
(0.8.0)

Buffer methods
(0.4.4):

alloc,
allocUnsafe,
byteLength,
compare,
concat,
from,
isBuffer,
isEncoding

Buffer prototype methods:
(0.4.4):

compare,
copy,
equals,
fill,
includes,
indexOf,
lastIndexOf,
readIntBE,
readInt8,
readInt16BE,
readInt32BE,
readIntLE,
readInt8,
readInt16LE,
readInt32LE,
readUIntBE,
readUInt8,
readUInt16BE,
readUInt32BE,
readUIntLE,
readUInt8,
readUInt16LE,
readUInt32LE,
readDoubleBE,
readDoubleLE,
readFloatBE,
readFloatLE,
subarray,
slice,
swap16,
swap32,
swap64,
toJSON,
toString,
write,
writeIntBE,
writeInt8,
writeInt16BE,
writeInt32BE,
writeIntLE,
writeInt8,
writeInt16LE,
writeInt32LE,
writeUIntBE,
writeUInt8,
writeUInt16BE,
writeUInt32BE,
writeUIntLE,
writeUInt8,
writeUInt16LE,
writeUInt32LE,
writeDoubleBE,
writeDoubleLE,
writeFloatBE,
writeFloatLE

Promise methods
(0.3.8):

ES6:
any
(0.6.2),
 all
(0.6.2),
 allSettled
(0.6.2),
reject,
resolve,
race (0.6.2)

Promise prototype methods
(0.3.8):

ES6:
catch,
finally,
then

Function prototype methods:

ES5.1:
apply,
bind,
call

RegExp prototype accessor properties:

flags (0.6.0),
global,
ignoreCase,
multiline,
source,
sticky (0.6.0)

RegExp prototype methods:

[@@replace]
(0.4.2),
[@@split]
(0.6.0)

ES5.1:
exec,
test,
toString

RegExp instance properties:

lastIndex

RegExp
ES9 named capture groups (0.3.2)

DataView prototype methods
(0.4.4):

ES6:
getFloat32,
getFloat64,
getInt16,
getInt32,
getInt8,
getUint16,
getUint32,
getUint8,
setFloat32,
setFloat64,
setInt16,
setInt32,
setInt8,
setUint16,
setUint32,
setUint8

Date methods:

ES5.1:
now,
parse,
UTC

Date prototype methods:

ES5.1:
getDate,
getDay,
getFullYear,
getHours,
getMilliseconds,
getMinutes,
getMonth,
getSeconds,
getTime,
getTimezoneOffset,
getUTCDate,
getUTCDay,
getUTCFullYear,
getUTCHours,
getUTCMilliseconds,
getUTCMinutes,
getUTCMonth,
getUTCSeconds,
toDateString,
toISOString,
toLocaleDateString,
toLocaleString,
toLocaleTimeString,
toTimeString,
toUTCString,
setDate,
setFullYear,
setHours,
setMinutes,
setMilliseconds,
setMonth,
setSeconds,
setTime,
setUTCDate,
setUTCFullYear,
setUTCHours,
setUTCMilliseconds,
setUTCMinutes,
setUTCMonth,
setUTCSeconds

JSON methods:

ES5.1:
parse,
stringify

Symbol methods
(0.7.6):

for,
keyfor

ES5.1 arguments object
(0.2.5)

ES6 rest parameters syntax (without destructuring support)
(0.2.7)

ES5.1 global functions:
decodeURI,
decodeURIComponent,
encodeURI,
encodeURIComponent,
isFinite,
isNaN,
parseFloat,
parseInt

Global functions (0.7.6):
atob,
btoa

Error objects:
Error,
EvalError,
InternalError,
RangeError,
ReferenceError,
SyntaxError,
TypeError,
URIError

clearTimeout
and
setTimeout
functions
(0.2.0)

File system methods:
fs.accessSync
(0.3.9),
fs.appendFileSync,
fs.closeSync,
fs.existsSync
(0.8.2),
fs.FileHandle
(0.7.7),
fs.fstatSync
(0.7.7),
fs.lstatSync
(0.7.1),
fs.mkdirSync
(0.4.2),
fs.openSync
(0.7.7),
fs.promises.open
(0.7.7),
fs.readdirSync
(0.4.2),
fs.readFileSync,
fs.readSync
(0.7.7),
fs.realpathSync
(0.3.9),
fs.renameSync
(0.3.4),
fs.rmdirSync
(0.4.2),
fs.symlinkSync
(0.3.9),
fs.unlinkSync
(0.3.9),
fs.writeFileSync
fs.writeSync
(0.7.7)

fs.promises API (0.3.9),
asynchronous version of file system methods.

Crypto methods
(0.2.0):
crypto.createHash,
crypto.createHmac

Query String
methods
(0.4.3):
querystring.decode,
querystring.encode,
querystring.escape,
querystring.parse,
querystring.stringify,
querystring.unescape

TextDecoder
methods
(0.4.3):
encoding,
fatal,
ignoreBOM,
decode

TextEncoder
methods
(0.4.3):
encode,
encodeInto

XML
methods
(0.7.10):
parse,
xml.c14n,
xml.exclusiveC14n

zlib
methods
(0.7.12):
deflateRawSync,
deflateSync,
inflateRawSync
inflateSync

ES6 modules support:
default export and
default import
statements
(0.3.0)

ES6 arrow functions
(0.3.1)

Template literals:
multiline strings, expression interpolation, nesting templates
(0.3.2)

Global objects
(0.3.3):

console
(0.8.2):
error,
info,
log,
time,
timeEnd,
warn

crypto
(0.7.0):
getRandomValues,
randomUUID
(0.9.7),
subtle.encrypt,
subtle.decrypt,
subtle.deriveBits,
subtle.deriveKey,
subtle.digest
subtle.exportKey
(0.7.10),
subtle.generateKey
(0.7.10),
subtle.importKey,
subtle.sign,
subtle.verify,
subtle.wrapKey
(0.9.7),
subtle.unwrapKey
(0.9.7)

globalThis alias
(0.3.8),

njs:
version,
version_number
(0.7.4),
dump,
memoryStats
(0.7.8),
on
(0.5.2)

process:
argv,
env,
pid,
ppid

nginx object methods:

HTTP Request:
r.done
(0.5.2),
r.error,
r.finish,
r.internalRedirect,
r.log,
r.return
(0.5.0),
r.send
(0.5.0),
r.sendBuffer
(0.5.2),
r.sendHeader,
r.setReturnValue
(0.7.0),
r.subrequest,
r.warn

Stream Session:
s.allow
(0.2.4),
s.decline
(0.2.4),
s.deny
(0.2.4),
s.done
(0.2.4),
s.error,
s.log,
s.off
(0.2.4),
s.on
(0.2.4),
s.send
(0.2.4),
s.sendDownstream
(0.7.8),
s.sendUpstream
(0.7.8),
s.setReturnValue
(0.7.0),
s.warn

Headers
(0.5.1):
append,
delete,
get,
getAll,
forEach,
has,
set

Request
(0.7.10):
arrayBuffer,
headers,
json,
text

Response
(0.5.1):
arrayBuffer,
headers,
json,
text

ngx
(0.5.0):
fetch
(0.5.1),
log

ngx.shared
(0.8.0):
add,
clear,
delete,
freeSpace,
get,
has,
incr,
items,
keys,
pop,
replace,
set,
size,
ttl
(0.9.7)

nginx object properties:

HTTP Request:
r.args,
r.headersIn,
r.headersOut,
r.httpVersion,
r.internal,
r.jsVarNames
(0.9.9),
r.method,
r.parent,
r.readRequestArrayBuffer
(0.9.9),
r.readRequestForm
(0.9.9),
r.readRequestJSON
(0.9.9),
r.readRequestText
(0.9.9),
r.rawHeadersIn
(0.4.1),
r.rawHeadersOut
(0.4.1),
r.rawVariables
(0.5.0),
r.remoteAddress,
r.requestBuffer
(0.5.0),
r.requestText,
r.responseBuffer
(0.5.0),
r.responseText
(0.5.0),
r.status,
r.uri,
r.variables
(0.2.8)

Stream Session:
s.jsVarNames
(0.9.9),
s.remoteAddress,
s.rawVariables
(0.5.0),
s.status
(0.5.2),
s.variables
(0.2.8)

Periodic Session
(0.8.1):
PeriodicSession.rawVariables,
PeriodicSession.variables

Request
(0.7.10):
bodyUsed,
cache,
credentials,
method,
mode,
url

Response
(0.5.1):
bodyUsed,
ok,
redirected,
status,
statusText,
type,
url

ngx
(0.5.0):
build
(0.8.0),
conf_file_path
(0.8.0),
conf_prefix
(0.7.8),
error_log_path
(0.8.0),
prefix
(0.8.0),
version
(0.8.0),
version_number
(0.8.0),
worker_id
(0.8.0)

ngx.shared
(0.8.0):
capacity,
name,
type


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>JavaScript Engine</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="../../../css/style_en.css">

Planning your ingress-nginx migration? Start here:
kubernetes.nginx.org.

<input class="side-menu" type="checkbox" id="side-menu">

english

русский

news

about

download

security

documentation

faq

books

community

enterprise

community forum (new)

x.com

blog

njs

ingress controller

gateway fabric

JavaScript Engine

Starting from version 0.8.6,
multiple JavaScript engines are supported.
To specify a particular engine, use the js_engine directive
available for both the
http
and
stream.
By default, the njs engine is used.

njs engine

njs is an embeddable JavaScript engine
developed as a part of the njs module.
See the Сompatibility section for details.

QuickJS engine

QuickJS is a lightweight,
embeddable JavaScript engine that supports
the ES2023 specification,
including features as modules, asynchronous generators, proxies and BigInt.

Since version 0.8.6,
a drop-in replacement for
njs/nginx objects
has been introduced
to ensure compatibility with the njs engine, with the following exceptions:

njs-specific API:
njs.dump(),
console.dump().

deprecated API:
require(),
use the import statement instead.

js_preload_object directive for
http
and
stream.

njs built-in modules status:

buffer:
since 0.8.6.

crypto:
since 0.8.10.

fs:
since 0.8.9.

querystring:
since 0.8.10.

WebCrypto:
since 0.8.10.

xml:
since 0.8.10.

zlib:
since 0.8.5.

njs built-in objects status:

process:
since 0.8.8.

TextDecoder:
since 0.8.10.

TextEncoder:
since 0.8.10.

nginx built-in objects status:

ngx.fetch:
since 0.9.1.

shared dictionary:
since 0.8.8.


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>nginx JavaScript module</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="../../../css/style_en.css">

Planning your ingress-nginx migration? Start here:
kubernetes.nginx.org.

<input class="side-menu" type="checkbox" id="side-menu">

english

русский

news

about

download

security

documentation

faq

books

community

enterprise

community forum (new)

x.com

blog

njs

ingress controller

gateway fabric

nginx JavaScript module

njs is an nginx module that extends the server's functionality through
JavaScript scripting, enabling the creation of custom
server-side logic and more.

Download and install

Changes

Reference

JavaScript Engine

Native modules

Examples

Security

Compatibility

Command-line interface

Understanding preloaded objects

Tested OS and platforms

ngx_http_js_module

ngx_stream_js_module

Writing njs code using TypeScript definition files

Using node modules with njs

Use cases

Complex access control and security checks in njs
before a request reaches an upstream server

Manipulating response headers

Writing flexible asynchronous content handlers and filters

See examples
for more njs use cases.

Basic HTTP Example

To use njs in nginx:

install njs scripting language

create an njs script file, for example, http.js.
See Reference
for the list of njs properties and methods.

function hello(r) {
    r.return(200, "Hello world!");
}

export default {hello};

in the nginx.conf file, enable
ngx_http_js_module module
and specify the
js_import
directive
with the http.js script file:

load_module modules/ngx_http_js_module.so;

events {}

http {
    js_import http.js;

    server {
        listen 8000;

        location / {
            js_content http.hello;
        }
    }
}

There is also a standalone command line utility
that can be used independently of nginx for njs development and debugging.

Tested OS and platforms

FreeBSD / amd64;

Linux / x86, amd64, arm64, ppc64el;

Solaris 11 / amd64;

macOS / x86_64;

Presentation at nginx.conf 2018


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Download and install</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="../../../css/style_en.css">

Planning your ingress-nginx migration? Start here:
kubernetes.nginx.org.

<input class="side-menu" type="checkbox" id="side-menu">

english

русский

news

about

download

security

documentation

faq

books

community

enterprise

community forum (new)

x.com

blog

njs

ingress controller

gateway fabric

Download and install

Installing as a Linux package
Building from the sources
     Adding QuickJS engine support
Building njs command-line utility

Installing as a Linux package

For Linux, njs modules
packages can be used:

nginx-module-njs — njs
dynamic modules

nginx-module-njs-dbg — debug symbols for the
nginx-module-njs package

After package installation, njs dynamic modules need to be loaded with the
load_module
directive:

load_module modules/ngx_http_js_module.so;

or

load_module modules/ngx_stream_js_module.so;

Building from the sources

The repository
with njs sources can be cloned with the following command
(requires Git client):

git clone https://github.com/nginx/njs

Then the modules should be compiled from
nginx root directory using the
--add-module configuration parameter:

./configure --add-module=path-to-njs/nginx

The modules can also be built as
dynamic:

./configure --add-dynamic-module=path-to-njs/nginx

Adding QuickJS engine support

Make sure you have built the QuickJS library:

git clone https://github.com/bellard/quickjs
cd quickjs
CFLAGS='-fPIC' make libquickjs.a

At the module compilation step, also
specify the include (-I) and library (-L)
paths with the
--with-cc-opt= and
--with-ld-opt= configuration parameters:

./configure --add-module=path-to-njs/nginx \
    --with-cc-opt="-I path-to-quickjs" \
    --with-ld-opt="-L path-to-quickjs"

Building njs command-line utility

To build only the njs command-line utility, run
./configure and make njs commands
from njs root directory.
After building, the utility is available as ./build/njs.


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Native modules</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="../../../css/style_en.css">

Planning your ingress-nginx migration? Start here:
kubernetes.nginx.org.

<input class="side-menu" type="checkbox" id="side-menu">

english

русский

news

about

download

security

documentation

faq

books

community

enterprise

community forum (new)

x.com

blog

njs

ingress controller

gateway fabric

Native modules

Overview
When to use native modules
Loading native modules in NGINX
Building native modules
Additional resources

Overview

Native modules allow loading C-based shared libraries (.so files)
into NGINX JavaScript for performance-critical operations
or system-level integrations.
This feature is available only with the
QuickJS engine
and is not supported by the njs native engine.
Native module support has been available
since 0.9.5.

When to use native modules

Native modules are useful in the following scenarios:

Performance-critical operations that exceed JavaScript capabilities

System-level integrations requiring C libraries

Leveraging existing C/C++ codebases

Native modules should be used for low-level primitives
rather than for complex business logic,
for example, for cryptographic operations (hashing, encryption),
data compression/decompression, binary protocol parsing,
high-performance string operations, or mathematical computations.
Complex application logic should remain in JavaScript
where it's easier to maintain, debug, and modify.

Limitations:

Native modules must be compiled for the same architecture as NGINX

Native modules run with full process privileges
and require careful security review

Loading native modules in NGINX

Native modules are loaded using these directives
specified in the main context:

js_load_http_native_module for HTTP context

js_load_stream_native_module for Stream context

Example configuration:

js_load_http_native_module /path/to/mylib.so;
js_load_http_native_module /path/to/other.so as myalias;

http {
    js_import main.js;

    server {
        listen 8000;
        location / {
            js_content main.handler;
        }
    }
}

Once loaded, the module can be imported in JavaScript code:

// Import by filename
import * as mylib from 'mylib.so';

// Import by alias
import * as myalias from 'myalias';

function handler(r) {
    let result = mylib.add(5, 10);
    r.return(200, `Result: ${result}\n`);
}

export default { handler };

Building native modules

Native modules must implement the js_init_module
function as the entry point.
This function is called by QuickJS when the module is loaded.

A complete example of a simple native module
that exports two functions:

#include <quickjs.h>

#define countof(x) (sizeof(x) / sizeof((x)[0]))

/* Add two numbers */
static JSValue
js_add(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    int a, b;

    if (argc < 2) {
        return JS_ThrowTypeError(ctx, "expected 2 arguments");
    }

    if (JS_ToInt32(ctx, &a, argv[0]) < 0) {
        return JS_EXCEPTION;
    }

    if (JS_ToInt32(ctx, &b, argv[1]) < 0) {
        return JS_EXCEPTION;
    }

    return JS_NewInt32(ctx, a + b);
}

/* Reverse a string */
static JSValue
js_reverse_string(JSContext *ctx, JSValueConst this_val, int argc,
    JSValueConst *argv)
{
    char        *reversed;
    size_t       i, len;
    JSValue      result;
    const char  *str;

    if (argc < 1) {
        return JS_ThrowTypeError(ctx, "expected 1 argument");
    }

    str = JS_ToCStringLen(ctx, &len, argv[0]);
    if (!str) {
        return JS_EXCEPTION;
    }

    reversed = js_malloc(ctx, len + 1);
    if (!reversed) {
        JS_FreeCString(ctx, str);
        return JS_EXCEPTION;
    }

    for (i = 0; i < len; i++) {
        reversed[i] = str[len - 1 - i];
    }
    reversed[len] = '\0';

    result = JS_NewString(ctx, reversed);

    js_free(ctx, reversed);
    JS_FreeCString(ctx, str);

    return result;
}

/* Module function list */
static const JSCFunctionListEntry js_module_funcs[] = {
    JS_CFUNC_DEF("add", 2, js_add),
    JS_CFUNC_DEF("reverseString", 1, js_reverse_string),
};

/* Module initialization */
static int
js_module_init(JSContext *ctx, JSModuleDef *m)
{
    return JS_SetModuleExportList(ctx, m, js_module_funcs,
                                  countof(js_module_funcs));
}

/* Required entry point */
JSModuleDef *
js_init_module(JSContext *ctx, const char *module_name)
{
    JSModuleDef  *m;

    m = JS_NewCModule(ctx, module_name, js_module_init);
    if (!m) {
        return NULL;
    }

    JS_AddModuleExportList(ctx, m, js_module_funcs,
                           countof(js_module_funcs));

    return m;
}

To compile the native module:

gcc -fPIC -shared -I/path/to/quickjs -o mymodule.so mymodule.c

where /path/to/quickjs is the directory
containing the QuickJS header files.

For proper memory tracking, always use QuickJS memory allocation functions
(js_malloc, js_free)
instead of standard library functions
(malloc, free).

Additional resources

For more information about the QuickJS C API:

QuickJS official website

QuickJS header file (quickjs.h)
contains comprehensive API documentation


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Using node modules with njs</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="../../../css/style_en.css">

Planning your ingress-nginx migration? Start here:
kubernetes.nginx.org.

<input class="side-menu" type="checkbox" id="side-menu">

english

русский

news

about

download

security

documentation

faq

books

community

enterprise

community forum (new)

x.com

blog

njs

ingress controller

gateway fabric

Using node modules with njs

Environment
Protobufjs
DNS-packet

Often, a developer wants to use 3rd-party code,
usually available as a library of some kind.
In the JavaScript world, the concept of a module is relatively new,
so there was no standard until recently.
Many platforms (browsers) still don't support modules, which makes code
reuse harder.
This article describes ways to reuse
Node.js code in njs.

Examples in this article use features that appeared in
njs
0.3.8

There is a number of issues
that may arise when 3rd-party code is added to njs:

Multiple files that reference each other and their dependencies

Platform-specific APIs

Modern standard language constructions

The good news is that such problems are not something new or specific to njs.
JavaScript developers face them daily
when trying to support multiple disparate platforms
with very different properties.
There are instruments designed to resolve the above-mentioned issues.

Multiple files that reference each other, and their dependencies

This can be solved by merging all the interdependent code into a single file.
Tools like
browserify or
webpack
accept an entire project and produce a single file containing
your code and all the dependencies.

Platform-specific APIs

You can use multiple libraries that implement such APIs
in a platform-agnostic manner (at the expense of performance, though).
Particular features can also be implemented using the
polyfill approach.

Modern standard language constructions

Such code can be transpiled:
this means performing a number of transformations
that rewrite newer language features in accordance with an older standard.
For example,  babel project
can be used to this purpose.

In this guide, we will use two relatively large npm-hosted libraries:

protobufjs —
a library for creating and parsing protobuf messages used by the
gRPC protocol

dns-packet —
a library for processing DNS protocol packets

Environment

This document mostly employs a generic approach
and avoids specific best practice advices concerning Node.js
and JavaScript.
Make sure to consult the corresponding package's manual
before following the steps suggested here.

First (assuming Node.js is installed and operational), let's create an
empty project and install some dependencies;
the commands below assume we are in the working directory:

$ mkdir my_project && cd my_project
$ npx license choose_your_license_here > LICENSE
$ npx gitignore node

$ cat > package.json <<EOF
{
  "name":        "foobar",
  "version":     "0.0.1",
  "description": "",
  "main":        "index.js",
  "keywords":    [],
  "author":      "somename <some.email@example.com> (https://example.com)",
  "license":     "some_license_here",
  "private":     true,
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  }
}
EOF
$ npm init -y
$ npm install browserify

Protobufjs

The library provides a parser
for the .proto interface definitions
and a code generator for message parsing and generation.

In this example, we will use the
helloworld.proto
file
from the gRPC examples.
Our goal is to create two messages:
HelloRequest and
HelloResponse.
We will use the
static
mode of protobufjs instead of dynamically generating classes, because
njs doesn't support adding new functions dynamically
due to security considerations.

Next, the library is installed and
the JavaScript code implementing message marshalling
is generated from the protocol definition:

$ npm install protobufjs
$ npx pbjs -t static-module helloworld.proto > static.js

Thus, the static.js file becomes our new dependency,
storing all the code we need to implement message processing.
The set_buffer() function contains code that uses the
library to create a buffer with the serialized
HelloRequest message.
The code resides in the code.js file:

var pb = require('./static.js');

// Example usage of protobuf library: prepare a buffer to send
function set_buffer(pb)
{
    // set fields of gRPC payload
    var payload = { name: "TestString" };

    // create an object
    var message = pb.helloworld.HelloRequest.create(payload);

    // serialize object to buffer
    var buffer = pb.helloworld.HelloRequest.encode(message).finish();

    var n = buffer.length;

    var frame = new Uint8Array(5 + buffer.length);

    frame[0] = 0;                        // 'compressed' flag
    frame[1] = (n & 0xFF000000) >>> 24;  // length: uint32 in network byte order
    frame[2] = (n & 0x00FF0000) >>> 16;
    frame[3] = (n & 0x0000FF00) >>>  8;
    frame[4] = (n & 0x000000FF) >>>  0;

    frame.set(buffer, 5);

    return frame;
}

var frame = set_buffer(pb);

To ensure it works, we execute the code using node:

$ node ./code.js
Uint8Array [
    0,   0,   0,   0,  12, 10,
   10,  84, 101, 115, 116, 83,
  116, 114, 105, 110, 103
]

You can see that this got us a properly encoded gRPC frame.
Now let's run it with njs:

$ njs ./code.js
Thrown:
Error: Cannot find module "./static.js"
    at require (native)
    at main (native)

Modules are not supported, so we've received an exception.
To overcome this issue, let's use browserify
or other similar tool.

An attempt to process our existing code.js file will result
in a bunch of JS code that is supposed to run in a browser,
i.e. immediately upon loading.
This isn't something we actually want.
Instead, we want to have an exported function that
can be referenced from the nginx configuration.
This requires some wrapper code.

In this guide, we use
njs cli in all examples for the sake of simplicity.
In real life, you will be using nginx njs module to run your code.

The load.js file contains the library-loading code that
stores its handle in the global namespace:

global.hello = require('./static.js');

This code will be replaced with merged content.
Our code will be using the "global.hello" handle to access
the library.

Next, we process it with browserify
to get all dependencies into a single file:

$ npx browserify load.js -o bundle.js -d

The result is a huge file that contains all our dependencies:

(function(){function......
...
...
},{"protobufjs/minimal":9}]},{},[1])
//# sourceMappingURL..............

To get final "njs_bundle.js" file we concatenate
"bundle.js" and the following code:

// Example usage of protobuf library: prepare a buffer to send
function set_buffer(pb)
{
    // set fields of gRPC payload
    var payload = { name: "TestString" };

    // create an object
    var message = pb.helloworld.HelloRequest.create(payload);

    // serialize object to buffer
    var buffer = pb.helloworld.HelloRequest.encode(message).finish();

    var n = buffer.length;

    var frame = new Uint8Array(5 + buffer.length);

    frame[0] = 0;                        // 'compressed' flag
    frame[1] = (n & 0xFF000000) >>> 24;  // length: uint32 in network byte order
    frame[2] = (n & 0x00FF0000) >>> 16;
    frame[3] = (n & 0x0000FF00) >>>  8;
    frame[4] = (n & 0x000000FF) >>>  0;

    frame.set(buffer, 5);

    return frame;
}

// functions to be called from outside
function setbuf()
{
    return set_buffer(global.hello);
}

// call the code
var frame = setbuf();
console.log(frame);

Let's run the file using node to make sure things still work:

$ node ./njs_bundle.js
Uint8Array [
    0,   0,   0,   0,  12, 10,
   10,  84, 101, 115, 116, 83,
  116, 114, 105, 110, 103
]

Now let's proceed further with njs:

$ njs ./njs_bundle.js
Uint8Array [0,0,0,0,12,10,10,84,101,115,116,83,116,114,105,110,103]

The last thing will be to use njs-specific API to convert
array into byte string, so it could be usable by nginx module.
We can add the following snippet before the line
return frame; }:

if (global.njs) {
    return String.bytesFrom(frame)
}

Finally, we got it working:

$ njs ./njs_bundle.js |hexdump -C
00000000  00 00 00 00 0c 0a 0a 54  65 73 74 53 74 72 69 6e  |.......TestStrin|
00000010  67 0a                                             |g.|
00000012

This is the intended result.
Response parsing can be implemented similarly:

function parse_msg(pb, msg)
{
    // convert byte string into integer array
    var bytes = msg.split('').map(v=>v.charCodeAt(0));

    if (bytes.length < 5) {
        throw 'message too short';
    }

    // first 5 bytes is gRPC frame (compression + length)
    var head = bytes.splice(0, 5);

    // ensure we have proper message length
    var len = (head[1] << 24)
              + (head[2] << 16)
              + (head[3] << 8)
              + head[4];

    if (len != bytes.length) {
        throw 'header length mismatch';
    }

    // invoke protobufjs to decode message
    var response = pb.helloworld.HelloReply.decode(bytes);

    console.log('Reply is:' + response.message);
}

DNS-packet

This example uses a library for generation and parsing of DNS packets.
This a case worth considering because the library and its dependencies
use modern language constructions not yet supported by njs.
In turn, this requires from us an extra step: transpiling the source code.

Additional node packages are needed:

$ npm install @babel/core @babel/cli @babel/preset-env babel-loader
$ npm install webpack webpack-cli
$ npm install buffer
$ npm install dns-packet

The configuration file, webpack.config.js:

const path = require('path');

module.exports = {
    entry: './load.js',
    mode: 'production',
    output: {
        filename: 'wp_out.js',
        path: path.resolve(__dirname, 'dist'),
    },
    optimization: {
        minimize: false
    },
    node: {
        global: true,
    },
    module : {
        rules: [{
            test: /\.m?js$$/,
            exclude: /(bower_components)/,
            use: {
                loader: 'babel-loader',
                options: {
                    presets: ['@babel/preset-env']
                }
            }
        }]
    }
};

Note we are using "production" mode.
In this mode webpack does not use "eval" construction
not supported by njs.
The referenced load.js file is our entry point:

global.dns = require('dns-packet')
global.Buffer = require('buffer/').Buffer

We start the same way, by producing a single file for the libraries:

$ npx browserify load.js -o bundle.js -d

Next, we process the file with webpack, which itself invokes babel:

$ npx webpack --config webpack.config.js

This command produces the dist/wp_out.js file, which is a
transpiled version of bundle.js.
We need to concatenate it with code.js
that stores our code:

function set_buffer(dnsPacket)
{
    // create DNS packet bytes
    var buf = dnsPacket.encode({
        type: 'query',
        id: 1,
        flags: dnsPacket.RECURSION_DESIRED,
        questions: [{
            type: 'A',
            name: 'google.com'
        }]
    })

    return buf;
}

Note that in this example generated code is not wrapped into function and we
do not need to call it explicitly.
The result is in the "dist" directory:

$ cat dist/wp_out.js code.js > njs_dns_bundle.js

Let's call our code at the end of a file:

var b = set_buffer(global.dns);
console.log(b);

And execute it using node:

$ node ./njs_dns_bundle_final.js
Buffer [Uint8Array] [
    0,   1,   1, 0,  0,   1,   0,   0,
    0,   0,   0, 0,  6, 103, 111, 111,
  103, 108, 101, 3, 99, 111, 109,   0,
    0,   1,   0, 1
]

Make sure this works as expected, and then run it with njs:

$ njs ./njs_dns_bundle_final.js
Uint8Array [0,1,1,0,0,1,0,0,0,0,0,0,6,103,111,111,103,108,101,3,99,111,109,0,0,1,0,1]

The response can be parsed as follows:

function parse_response(buf)
{
    var bytes = buf.split('').map(v=>v.charCodeAt(0));

    var b = global.Buffer.from(bytes);

    var packet = dnsPacket.decode(b);

    var resolved_name = packet.answers[0].name;

    // expected name is 'google.com', according to our request above
}


<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"><title>Understanding preloaded objects</title><meta name="viewport" content="width=device-width,initial-scale=1"><link rel="stylesheet" href="../../../css/style_en.css">

Planning your ingress-nginx migration? Start here:
kubernetes.nginx.org.

<input class="side-menu" type="checkbox" id="side-menu">

english

русский

news

about

download

security

documentation

faq

books

community

enterprise

community forum (new)

x.com

blog

njs

ingress controller

gateway fabric

Understanding preloaded objects

For each incoming request njs creates a separate virtual machine.
This brings a lot of benefits such as predictable memory consumption
or requests isolation.
However, as all requests are isolated,
if a request handler needs to access some data,
it has to read it by itself.
This is not efficient especially when the amount of data is large.

To address this limitation,
a preloaded shared object was introduced.
Such objects are created immutable and do not have prototype chains:
their values cannot be changed, properties cannot be added or removed.

Working with preload objects

Here are some examples of how to work with a preload object in njs:

access properties by name:

preloaded_object.prop_name
preloaded_object[prop_name]

enumerate properties:

for (i in preloaded_object_name) {
    ...
}

apply non-modifying built-in methods using call():

Array.prototype.filter.call(preloaded_object_name, ...)