Skip to content

fix(neon): Allow extracting Json<Option<T>> when value is undefined#1130

Merged
kjvalencik merged 1 commit intomainfrom
kv/json-option
Jan 12, 2026
Merged

fix(neon): Allow extracting Json<Option<T>> when value is undefined#1130
kjvalencik merged 1 commit intomainfrom
kv/json-option

Conversation

@kjvalencik
Copy link
Copy Markdown
Member

@kjvalencik kjvalencik commented Jan 2, 2026

In JavaScript, it's very common to have optional arguments, especially with options object. Consider the following:

function createClient(opts?: Partial<ClientOptions>);

In this case, a user can call createClient() without any options or they can call it with any subset of the keys from ClientOptions. If we want to model this in Neon, we can use the Json extractor.

#[dervie(Default, Deserialize)]
struct ClientOptions {
    host: Option<String>,
    /* ... */
}

#[neon::export]
fn create_client(Json(opts): Json<ClientOptions>) {}

This already works well, except for case where opts isn't provided at all. For that, we want to Option<T> wrap the options.

fn create_client(Json(opts): Json<Option<ClientOptions>>) {
    let opts = opts.unwrap_or_default();
}

This works if the user explicitly passes null for opts (since it is serialized as "null"), but fails if it's undefined (not passed at all) because of the behavior of JSON.stringify. This PR fixes it to match expectations.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented Jan 2, 2026

🐰 Bencher Report

Branchkv/json-option
Testbedubuntu-latest
Click to view all benchmark results
BenchmarkLatencyBenchmark Result
nanoseconds (ns)
(Result Δ%)
Lower Boundary
nanoseconds (ns)
(Limit %)
Upper Boundary
nanoseconds (ns)
(Limit %)
ThroughputBenchmark Result
operations / second (ops/s)
(Result Δ%)
Lower Boundary
operations / second (ops/s)
(Limit %)
Upper Boundary
operations / second (ops/s)
(Limit %)
JsFunction::bind📈 view plot
🚷 view threshold
209.48 ns
(+3.04%)Baseline: 203.29 ns
182.96 ns
(87.34%)
406.59 ns
(51.52%)
📈 view plot
🚷 view threshold
4,753,296.93 ops/s
(-3.27%)Baseline: 4,913,759.54 ops/s
0.00 ops/s
(0.00%)
5,405,135.49 ops/s
(87.94%)
JsFunction::call📈 view plot
🚷 view threshold
219.68 ns
(+1.80%)Baseline: 215.80 ns
194.22 ns
(88.41%)
431.61 ns
(50.90%)
📈 view plot
🚷 view threshold
4,550,537.08 ops/s
(-1.51%)Baseline: 4,620,462.24 ops/s
0.00 ops/s
(0.00%)
5,082,508.47 ops/s
(89.53%)
JsFunction::call_with📈 view plot
🚷 view threshold
211.44 ns
(+4.10%)Baseline: 203.10 ns
182.79 ns
(86.45%)
406.21 ns
(52.05%)
📈 view plot
🚷 view threshold
4,726,878.67 ops/s
(-3.64%)Baseline: 4,905,637.96 ops/s
0.00 ops/s
(0.00%)
5,396,201.76 ops/s
(87.60%)
auto-exported-noop📈 view plot
🚷 view threshold
28.59 ns
(-3.49%)Baseline: 29.62 ns
26.66 ns
(93.25%)
59.25 ns
(48.26%)
📈 view plot
🚷 view threshold
34,951,722.76 ops/s
(+2.28%)Baseline: 34,170,940.04 ops/s
0.00 ops/s
(0.00%)
37,588,034.05 ops/s
(92.99%)
hello-world📈 view plot
🚷 view threshold
58.39 ns
(+0.15%)Baseline: 58.30 ns
52.47 ns
(89.87%)
116.60 ns
(50.07%)
📈 view plot
🚷 view threshold
16,752,208.51 ops/s
(-2.44%)Baseline: 17,170,662.37 ops/s
0.00 ops/s
(0.00%)
18,887,728.61 ops/s
(88.69%)
manually-exported-noop📈 view plot
🚷 view threshold
27.82 ns
(-2.03%)Baseline: 28.39 ns
25.55 ns
(91.86%)
56.79 ns
(48.99%)
📈 view plot
🚷 view threshold
35,839,475.97 ops/s
(+1.79%)Baseline: 35,210,196.62 ops/s
0.00 ops/s
(0.00%)
38,731,216.28 ops/s
(92.53%)
🐰 View full continuous benchmarking report in Bencher

@codecov
Copy link
Copy Markdown

codecov Bot commented Jan 2, 2026

Codecov Report

❌ Patch coverage is 60.00000% with 2 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.16%. Comparing base (22098e4) to head (c53500b).
⚠️ Report is 2 commits behind head on main.

Files with missing lines Patch % Lines
crates/neon/src/types_impl/extract/json.rs 60.00% 0 Missing and 2 partials ⚠️
Additional details and impacted files
@@           Coverage Diff           @@
##             main    #1130   +/-   ##
=======================================
  Coverage   83.16%   83.16%           
=======================================
  Files          80       80           
  Lines        5743     5739    -4     
  Branches     5743     5739    -4     
=======================================
- Hits         4776     4773    -3     
  Misses        848      848           
+ Partials      119      118    -1     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Collaborator

@dherman dherman left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks nice! It's subtle so I'm still not 100% confident about it, but I definitely know I want it.

I left a couple of questions and one suggestion for an additional test in my review.

Comment thread test/napi/src/js/extract.rs
Comment thread crates/neon/src/types_impl/extract/json.rs
Comment thread crates/neon/src/types_impl/extract/json.rs
@kjvalencik kjvalencik merged commit 303eb3c into main Jan 12, 2026
10 checks passed
@kjvalencik kjvalencik deleted the kv/json-option branch January 12, 2026 17:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants