🚀 Node-API Part-10: Loading ArkTS Modules Dynamically in HarmonyOS Using Node-API
🧩 Introduction
As HarmonyOS continues to mature, hybrid development — bridging the performance of native C++ with the flexibility of ArkTS (Ark TypeScript) — is becoming a powerful paradigm. A cornerstone of this interoperability is the Node-API, which allows native modules to dynamically load and interact with ArkTS modules at runtime.
In this article, we explore how to use napi_load_module_with_info, a versatile function that enables C++ code to import and invoke logic defined in ArkTS modules, unlocking new layers of runtime dynamism and modular design.
🛠️ Why Load ArkTS Modules from Native Code?
There are many scenarios where bridging native C++ code and ArkTS modules proves beneficial:
- Performance & Flexibility: Offload performance-intensive logic to native code, while keeping configuration or business logic in TypeScript.
- Plugin Architectures: Dynamically load new functionality without recompiling native binaries.
- Cross-Layer Communication: Let native system modules interact with high-level ArkTS logic — enabling cleaner separation of concerns.
Thanks to HarmonyOS’s robust Node-API support, this is now possible using napi_load_module_with_info.
📦 What is napi_load_module_with_info?
The function napi_load_module_with_info dynamically loads an ArkTS module into a native runtime context.
It supports a wide range of modules:
- Local project .ets files
- HAR libraries
- Remote or ohpm-managed packages
- System APIs (e.g., @ohos.hilog)
- Native libraries (.so files) that expose JavaScript-accessible symbols
Parameters:
- path: Module path or name (e.g., "entry/src/main/ets/Test" or "@ohos/hypium")
- module_info: Combines bundleName/moduleName, typically from app.json5 and module.json5
Post-loading Access:
- Use napi_get_named_property to access exported functions
- Use napi_get_property for constants or object values
- Use napi_call_function to invoke ArkTS logic from C++
🧱 Real-World Use Cases
Here are practical examples where dynamic module loading enhances development flexibility:
Loading Local Modules: Load .ets files for reusable helpers, configuration, or business logic.
Using Third-Party Packages: Seamlessly bring in packages like @ohos/hypium and use them in native workflows.
Accessing System APIs: Dynamically access built-in modules such as @ohos.hilog for logging or system utilities.
Working with Native Libraries:Use .so files as modules and call C/C++ functions via JavaScript-style bindings.
static napi_value LoadModuleFile(napi_env env, napi_callback_info info) {
napi_value result;
// 1. Call napi_load_module_with_info to load the module from the Test.ets file.
napi_status status =
napi_load_module_with_info(env, "entry/src/main/ets/Test", "com.huawei.myapplication/entry", &result);
if (status != napi_ok) {
return nullptr;
}
napi_value testFn;
// 2. Call napi_get_named_property to obtain the test function.
napi_get_named_property(env, result, "test", &testFn);
// 3. Call napi_call_function to invoke the test function.
napi_call_function(env, result, testFn, 0, nullptr, nullptr);
napi_value value;
napi_value key;
std::string keyStr = "value";
napi_create_string_utf8(env, keyStr.c_str(), keyStr.size(), &key);
// 4. Call napi_get_property to obtain a variable value.
napi_get_property(env, result, key, &value);
return result;
}
static napi_value LoadModuleLibrary1(napi_env env, napi_callback_info info) {
napi_value result;
// 1. Call napi_load_module_with_info to load library.
napi_status status = napi_load_module_with_info(env, "library", "com.huawei.myapplication/entry", &result);
if (status != napi_ok) {
return nullptr;
}
napi_value testFn;
// 2. Call napi_get_named_property to obtain the test function.
napi_get_named_property(env, result, "test", &testFn);
// 3. Call napi_call_function to invoke the test function.
napi_call_function(env, result, testFn, 0, nullptr, nullptr);
napi_value value;
napi_value key;
std::string keyStr = "value";
napi_create_string_utf8(env, keyStr.c_str(), keyStr.size(), &key);
// 4. Call napi_get_property to obtain a variable value.
napi_get_property(env, result, key, &value);
return result;
}
LoadModule.cpp
⚙️ Setup and Configuration Notes
To ensure smooth module loading, consider the following setup:
- Declare Modules: List required packages under runtimeOnly.packages or runtimeOnly.sources in build-profile.json5.
- Correct Paths: Ensure the module_info and file paths align with those in app.json5, module.json5, or oh-package.json5.
- Dependency Declaration: Even if transitively used, explicitly declare third-party dependencies in your consuming module’s config.
🚨 Common Failure Scenarios
Dynamic module loading introduces flexibility, but also potential pitfalls. Here’s how errors typically manifest:
- napi_generic_failure: Likely due to invalid paths or misconfigured project metadata.
- napi_pending_exception: A runtime issue—missing files, wrong entry points, or broken imports.
- cppcrash: Indicates a low-level system failure. These are rare but should be logged and reported.
Always implement robust error handling and fallback mechanisms when dealing with dynamic imports.
🧠 Conclusion
The ability to dynamically load ArkTS modules using napi_load_module_with_info represents a significant advancement in HarmonyOS development. It empowers you to write clean, modular, and scalable applications—combining the speed of C++ with the expressiveness of TypeScript.
Whether you’re building plugin-ready applications, optimizing performance, or orchestrating high-level ArkTS logic from native threads, this API gives you the tools to do so efficiently and elegantly.
Mastering this pattern will place you at the forefront of hybrid app development on HarmonyOSNext. 💡
📚 Resources
Document
The OpenCms demo, brought to you by Alkacon Software.developer.huawei.com
Top comments (0)