I'm using mongocxx and bsoncxx libraries in c++. How can i pass find query result (or any native type) to nodejs with node-addon-api ?
Value Find(Callbackinfo& info{
mongocxx::instance inst{};
mongocxx::client conn{mongocxx::uri{}};
auto db = conn["test"];
auto cursor = db["testcollection"].find({});
return cursor ??
}
Any types returned to JavaScript has to be bridged to Napi::Value type in node-addon-api.
Though it depends on your usage case: if the value was expected to be manipulated in JavaScript.
If not, attaching it to an arbitrary JavaScript object just works.
Hi @emreyalvac ,
Take a look at ObjectWrap. It allows you to "wrap" any native C++ object to be used within Node. This will basically create a new JS class that you can use with constructor, prototype properties, etc. If you want to have some functionality that the JS APIs can access (eg. perhaps this mongocxx::cursor has some APIs like next() that you would want to access within JS), ObjectWrap would be a good choice for you.
If you just want to have simple NAPI Value that represents the underlying C++ mongocxx::cursor data, you could also use External. This basically creates a Napi::Value that has any arbitrary void* data associated with it. You can only access this data within your C++ native module however. This object will be presented to your JS code as a simple external object that has no real properties etc. (of course, you can provide Napi::Functions that use an Napi::External, but might be easier for the ObjectWrap approach depending on your use cases)
Does this help?
I want add that her you can find some simple examples on how to use Napi::ObjectWrap:
I used it like this, but the return was like that
std::string testData = "test";
Value CreateExternal(const CallbackInfo &info) {
return External<std::string>::New(info.Env(), &testData);
}
js:
const addon = require('bindings')('native.node')
var a = addon.s();
console.log(a);
//Output
[External]
Hi @emreyalvac,
when you return a value like Napi::External it will be marked like external object and you can access and manipulate it only in the C++ side. The Napi::Externalis used to allocate a JavaScript value with external data attached to it, data that you can use later on the native side. On the JavaScript side when you try to print the properties of Napi::External object you will get the output you reported.
You can use Napi::External to return your native object, but you need to create a native function if you want use it:
#include <napi.h>
std::string testData = "test";
Napi::Value CreateExternal(const Napi::CallbackInfo &info) {
return Napi::External<std::string>::New(info.Env(), &testData);
}
Napi::Value ToString(const Napi::CallbackInfo &info) {
Napi::External<std::string> external = info[0].As<Napi::External<std::string>>();
return Napi::String::New(info.Env(), external.Data()->c_str());
}
Napi::Object Init(Napi::Env env, Napi::Object exports) {
exports.Set(Napi::String::New(env, "createExternal"),
Napi::Function::New(env, CreateExternal));
exports.Set(Napi::String::New(env, "toString"),
Napi::Function::New(env, ToString));
return exports;
}
NODE_API_MODULE(addon, Init)
const addon = require('bindings')('addon');
const external = addon.createExternal()
const string = addon.toString(external)
console.log(string)
@NickNaso
Thank you for your answer but i can't use it with mongocxx lib find type ?
Hi @emreyalvac ,
Your question is really vague. Really, anything is possible, you just have to use the correct wrappers. Can you be more specific with what you are trying to accomplish? I am not familiar with the mongocxx library.
Hi @KevinEady ,
Sorry for the uncertainty.
I'm using mongocxx library for use mongodb in c++. I'm running Find function in c++. It's returning mongocxx::cursor type but i can't returning this type to nodejs.
Value Find(const CallbackInfo &info) {
std::string collection = info[0].As<Napi::String>();
mongocxx::uri uri{"mongodb://localhost:27017/?minPoolSize=100&maxPoolSize=150"};
mongocxx::pool pool{uri};
auto client = pool.acquire();
auto cursor = (*client)["testdb"][collection].find({});
return External<mongocxx::cursor>::New(info.Env(), &cursor);
}
Hi @emreyalvac ,
You are returning an External to JS. You need to also create C++ methods that will take this Value as an External<mongocxx:cursor> and get its .Data() pointer (which would be this address &cursor). This is what @NickNaso 's example does with an External<std::string>: his C++ method CreateExternal returns to JS a Value (specifically, this External<std::string>) and his C++ method ToString takes the first argument in passed in JS as External, gets its .Data(), and performs actions).
Note: with your implementation of Find() you are creating values on the stack (these uri and pool values), and then passing this stack-allocated pointer to External::New. I do not think this will work as you intend it to -- this value is no longer valid once this function returns.
With External, you will need to use proper memory management to the allocate memory in this method and (generally) free it once the External is no longer used inside JavaScript. The External::New constructor takes a callback that is ran on the main thread when the External is about to be garbage collected.
I would highly look into ObjectWrap because this will handle a lot of the memory management for you. For example, your C++ class' constructor correlates to the JS constructor, and here you could create the mongocxx::uri uri{"mongodb://localhost:27017/?minPoolSize=100&maxPoolSize=150"}; which would be stored on the instance being constructed, if that makes sense. Then in your class' destructor, do whatever cleanup necessary (again, destructor is ran when your object is being GC'd)
Does this all help?
Hi @KevinEady ,
I already did that way. How can i return ?
Value Find(const CallbackInfo &info) {
std::string collection = info[0].As<Napi::String>();
mongocxx::uri uri{"mongodb://localhost:27017/?minPoolSize=100&maxPoolSize=150"};
mongocxx::pool pool{uri};
auto client = pool.acquire();
auto cursor = (*client)["test"][collection].find({});
return External<mongocxx::cursor>::New(info.Env(), &cursor);
}
Value GetFind(const CallbackInfo &info) {
External<mongocxx::cursor> data = info[0].As<External<mongocxx::cursor>>();
return ???
}
data.Data() will give you the pointer mongocxx::cursor* that you defined via External<mongocxx::cursor>::New(info.Env(), &cursor); in Find() , but see my comment regarding the validity of this pointer's data.
Then, you will need to use whatever mongocxx APIs there are to get whatever values you want (strings, numbers, etc) and create JavaScript values via Object::New, Number::New, String::New, etc... See Basic Types
So, for example, if there was some function on mongocxx::cursor like int numRows(), you would return from your GetFind() function something like return Number::New(info.Env(), data.Data()->numRows());
Hi @emreyalvac ,
Does this solve your question? Can we close this issue?
@KevinEady
Unfortunately, I couldn't resolve.
Hi @emreyalvac ,
Is there something we can help with then? Do you have more questions? What can we do so we can close this issue?
@NickNaso
Hi @emreyalvac,
when you return a value likeNapi::Externalit will be marked like external object and you can access and manipulate it only in the C++ side. TheNapi::Externalis used to allocate a JavaScript value with external data attached to it, data that you can use later on the native side. On the JavaScript side when you try to print the properties ofNapi::Externalobject you will get the output you reported.
You can useNapi::Externalto return your native object, but you need to create a native function if you want use it:#include <napi.h> std::string testData = "test"; Napi::Value CreateExternal(const Napi::CallbackInfo &info) { return Napi::External<std::string>::New(info.Env(), &testData); } Napi::Value ToString(const Napi::CallbackInfo &info) { Napi::External<std::string> external = info[0].As<Napi::External<std::string>>(); return Napi::String::New(info.Env(), external.Data()->c_str()); } Napi::Object Init(Napi::Env env, Napi::Object exports) { exports.Set(Napi::String::New(env, "createExternal"), Napi::Function::New(env, CreateExternal)); exports.Set(Napi::String::New(env, "toString"), Napi::Function::New(env, ToString)); return exports; } NODE_API_MODULE(addon, Init)const addon = require('bindings')('addon'); const external = addon.createExternal() const string = addon.toString(external) console.log(string)
That should be an official example and your explanation in the readme.
I'm glad I found this post just before opening a ticket.
Thanks!
Hello! I am currently trying to do moreless the same. The problem is that I cannot integrate the mongocxx drivers in the addon; when I run node-gyp rebuild, I get an error message that says "cannot open source file", with all the mongocxx driver libraries that I include. Could you please tell me how you did it? @emreyalvac I would be really grateful
Most helpful comment
Hi @emreyalvac,
when you return a value like
Napi::Externalit will be marked like external object and you can access and manipulate it only in the C++ side. TheNapi::Externalis used to allocate a JavaScript value with external data attached to it, data that you can use later on the native side. On the JavaScript side when you try to print the properties ofNapi::Externalobject you will get the output you reported.You can use
Napi::Externalto return your native object, but you need to create a native function if you want use it: