Search code examples
node.jsnode.js-addonnode-ffi

Understanding tools to write a Node.js native addon


I need to create a Node.js native addon from some existing C code and I see that there are multiple ways to do that: using the new N-API (or the easier C++ native addon API) or using Node FFI. Also, I am not familiar with tools such as node-gyp, and I have some questions to help understand how I can integrate the C code into my Node application:

  1. Is there a preferred method between N-API and Node FFI? I guess both have their pros and cons, what are they?
  2. Is it required to compile the C code using node-gyp? How should I do if I already have a library available but not compiled with node-gyp (eg. .dylib) and I want to use it in my Node application?

Solution

  • 1 - N-API is, in my perspective, the preferred method. It's faster than Node FFI and is part of the core node distribution. Node FFI's main advantage (from my quick read; I haven't used it) is that it allows making calls without writing c/c++ code. Whether you use N-API or the C++ native addon API is more a matter of preference on your part. The C++ API does remove some of the repetitive coding required by N-API. But if you're not using C++ to begin with that's probably not a good reason to change.

    2 - you do not need to compile external libraries. Just put them in the binding.gyp file's libraries section as follows:

    {
    'targets': [{
        'target_name': 'addon-name',
        'cflags!': [ '-fno-exceptions' ],
        'cflags_cc!': [ '-fno-exceptions' ],
        'xcode_settings': { 'GCC_ENABLE_CPP_EXCEPTIONS': 'YES',
            'CLANG_CXX_LIBRARY': 'libc++',
            'MACOSX_DEPLOYMENT_TARGET': '10.7',
        },
        'msvs_settings': {
            'VCCLCompilerTool': { 'ExceptionHandling': 1 },
        },
        'include_dirs': [
        '<!@(node -p "require(\'node-addon-api\').include")',
        ],
        'sources': [
            'src/addon-name.cc'
        ],
        'conditions': [
            ['OS in "linux"', {
            'include_dirs': [
                '<!@(node -p "require(\'node-addon-api\').include")',
                '<(module_root_dir)/'
            ],
            'libraries': [
                '-ldylib',
                '-L<(module_root_dir)/dylib/',
                '-Wl,-rpath-link,<(module_root_dir)/dylib/',
                '-Wl,-rpath,\$$ORIGIN/../../dylib/'
            ],
            }]
        ]
        }]
    }
    

    Most of the boilerplate for bindings.gyp was generated by the conversion tool which is part of the node-addon-api package. I included my own libraries section because I distribute a library with my package and figuring out how to embed $ORIGIN as the location of the file was hard-won. So if you plan on distributing the library this provides a headstart on loading it when not installed in a system directory.