Skip to content

Commit ee439f7

Browse files
committed
Dosc about napi
1 parent 2e04a17 commit ee439f7

1 file changed

Lines changed: 61 additions & 0 deletions

File tree

docs/src/internal/napi.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
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

Comments
 (0)