This document explains why we couldn't directly use Node.js's C++ SQLite implementation and instead had to create a compatibility layer.
Node.js's SQLite implementation (node_sqlite.cc) is deeply integrated with Node.js internals that are not accessible to addon developers. These internals include:
// Node.js internal code
class DatabaseSync : public BaseObject {
// Uses internal lifecycle management
};
BaseObject// Extensive use of Environment throughout
Environment* env = Environment::GetCurrent(args);
env->isolate()
env->context()
env->sqlite_*_string() // Cached strings
The implementation requires headers that aren't installed with Node.js:
node_internals.hbase_object-inl.henv-inl.hnode_mem-inl.hutil-inl.h// Internal error macros
THROW_ERR_INVALID_ARG_TYPE(env, "Database must be a string");
CHECK_ERROR_OR_THROW(env, r, SQLITE_OK, void());
void MemoryInfo(MemoryTracker* tracker) const override {
tracker->TrackField("stmt", stmt_);
}
class NodeSqliteWork : public ThreadPoolWork {
// Uses internal libuv thread pool
};
Instead of rewriting from scratch, we created a shim layer (src/shims/) that provides minimal implementations of these Node.js internals:
// Our shim
template <typename T>
class BaseObject : public Napi::ObjectWrap<T> {
// Simplified using N-API patterns
};
// Our shim provides just what SQLite needs
class Environment {
Napi::Env env_;
std::unordered_map<std::string, napi_ref> string_cache_;
// Basic functionality only
};
// Convert internal macros to N-API
#define THROW_ERR_INVALID_ARG_TYPE(env, msg) \
Napi::TypeError::New(env, msg).ThrowAsJavaScriptException()
// Stub implementation since we don't need heap snapshots
class MemoryTracker {
template<typename T> void TrackField(const char*, T) {}
};
// Use standard C++ threading with ThreadSafeFunction
void QueueWork(std::function<void()> work) {
std::thread([work]() { work(); }).detach();
}
The shim layer approach allows us to ship a standalone npm package that:
While this required significant effort to create the compatibility layer, it provides the best balance between compatibility, maintainability, and usability.