Native modules

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

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