|
| 1 | +# Napi (and napi-rs) |
| 2 | + |
| 3 | +## Distinction |
| 4 | + |
| 5 | +Node-API and napi-rs are two different things. When encountering just "napi" in the documentation (or PRs/issues), |
| 6 | +what is meant in most cases is Node-API (although there may be some exceptions, especially in old docs). |
| 7 | +However, when you encounter napi in the code (mostly Rust code), this refers to napi-rs. |
| 8 | + |
| 9 | +In napi-rs there are different kinds of interfaces. Some of the interfaces napi-rs exposes are just 1-to-1 mappings from Node-API (mostly `napi::sys`). |
| 10 | +Then there are low-cost abstractions over Node-API, like `(To/From)NapiValue` traits and `napi::Env`. |
| 11 | +Then there are high-cost interfaces. Some of those add overhead that we would need to handle on our own (like **sync** functions: functions with the `#[napi]` macro). |
| 12 | +Others add high-cost overhead that we can avoid by using lower-abstraction-level approaches to achieve sometimes very significant performance improvements. |
| 13 | +This often comes at the cost of some limitations, however those limitations rarely pose a problem for our use cases. |
| 14 | + |
| 15 | +## Our interfaces |
| 16 | + |
| 17 | +The main benefit of high-cost napi-rs interfaces is that they provide abstractions that very significantly reduce required verbosity. |
| 18 | +This section briefly describes the available internal interfaces and their use cases: |
| 19 | + |
| 20 | +### To Napi Value |
| 21 | + |
| 22 | +When you want to convert a structured object from Rust to JS, use helpers defined in [to_napi_obj.rs](../../../src/utils/to_napi_obj.rs). |
| 23 | +Helpers defined there allow you to convert: |
| 24 | + |
| 25 | +- Rust structs and enums with values in variants to JS |
| 26 | +- Rust maps (String -> Any) into JS objects |
| 27 | + |
| 28 | +Details on how to use them are present in the code docs for that file. |
| 29 | +Those helpers are a specialized replacement for [napi-rs classes](https://napi.rs/docs/concepts/class). |
| 30 | +They provide better performance with a reasonable interface. |
| 31 | +However, in performance-critical sections, it's best to return an [array of values](https://github.com/scylladb/nodejs-rs-driver/blob/2e04a1701e4cfa039bac35c96d8b84b3a5f93867/src/paging.rs#L53-L73) |
| 32 | +and then construct the desired object [on the JS side](https://github.com/scylladb/nodejs-rs-driver/blob/f4d6a68641bec162f066641b599eb31ec6efc0fa/lib/client.js#L354-L377). |
| 33 | + |
| 34 | +### From Napi Value |
| 35 | + |
| 36 | +Similar to the previous section, we have helpers for converting JS objects to Rust structs. To see more, go to [from_napi_obj.rs](../../../src/utils/from_napi_obj.rs). |
| 37 | +This macro provides a much cleaner interface than [napi-rs classes](https://napi.rs/docs/concepts/class). |
| 38 | +Compared to napi-rs classes, it allows you to convert arbitrary JS objects with the desired structure, rather than only objects created through the napi-rs layer. |
| 39 | + |
| 40 | +When using structs from this macro, all fields are converted from JS to Rust. This means that when you have a struct with a high number of fields but use only a few of them, |
| 41 | +you will still pay the cost of converting all unused fields. |
| 42 | + |
| 43 | +### Casyn (async bridge) |
| 44 | + |
| 45 | +This is an open improvement that attempts to cut the very high CPU cost of synchronization in async functions. |
| 46 | +You can see the benchmarks for this feature in [#414](https://github.com/scylladb/nodejs-rs-driver/pull/414). |
| 47 | +TODO: finish this section once #414 is merged. |
| 48 | + |
| 49 | +### JSResults |
| 50 | + |
| 51 | +We use a custom JSResult for results that may end with an error. This replaces napi-rs Results, as those do not allow for custom errors. |
| 52 | +We need custom errors since the errors returned by the Rust driver contain additional information. For now we just pass the error name. |
| 53 | +We then need to use JSResult over regular Result since we cannot implement the ToNapiValue trait on a regular Result. |
| 54 | +This complicates the code slightly but is the solution we found with the lowest verbosity. |
| 55 | + |
| 56 | +## The napi problem |
| 57 | + |
| 58 | +The napi-rs library is not perfect. We have already [found and fixed](https://github.com/napi-rs/napi-rs/issues?q=author%3Aadespawn) multiple bugs in the library. |
| 59 | +The library is developed with heavy LLM usage. The quality and readability of the code in some places is less than ideal. |
| 60 | +However, this is still the best option we have. To reduce the impact of problems with the napi-rs code, we avoid using high-abstraction parts of napi-rs, |
| 61 | +and where possible switch to self-written, specialized abstractions. |
0 commit comments