· via dev.to (home feed)
Dynoxide 1.4.0 adds graceful embedded-server shutdown and JSON Lines import
Dynoxide 1.4.0 adds .jsonl import support, a graceful-shutdown API for its embedded HTTP server, and a batch of DynamoDB parity fixes for updates, scans and queries.

Dynoxide, a tool for running a DynamoDB-compatible server locally, has shipped version 1.4.0. According to the release notes on dev.to, it is a smaller release than 1.3.0, combining a community contribution to the import command, a graceful-stop mechanism for the embedded HTTP server, and a round of fixes that bring update and scan behaviour closer to the real DynamoDB.
JSON Lines import
The import command gains support for JSON Lines files, contributed by Sam Chung. As the release notes recount, Chung was testing import, realised the .json files the tool consumes are actually JSON Lines rather than ordinary JSON, renamed them to .jsonl so editors would handle them properly, and then wired the extension into Dynoxide itself.
dynoxide import now accepts .jsonl and .jsonl.gz alongside the existing .json and .json.gz. Files can be arranged either in a DynamoDB export layout — a directory holding a per-table folder with a data subfolder — or simply placed together in a flat directory.
Graceful shutdown for embedded servers
For developers running Dynoxide's HTTP server inside their own Rust code, stopping it previously meant aborting the underlying task. The release notes explain the knock-on effect: connection tasks kept their hold on the database, so a file-backed database's files stayed open — and Windows will not delete a file that is still open, which complicates test cleanup.
dynoxide-rs 3.1.0 introduces server::serve_with_shutdown, which takes a future that acts as the stop signal. When that future resolves, the server stops accepting new connections, closes idle keep-alive connections, lets in-flight requests finish, and then returns. One caveat from the documentation: the final connection can release the database a moment after the call returns, so file deletion may need a short retry loop. The dynoxide start command is now built on the same mechanism, with Ctrl-C and SIGTERM working as before.
Parity fixes for updates and scans
Most of the remaining work targets UpdateItem, PartiQL UPDATE, Scan and Query fidelity. The changes most likely to surface in a test:
- An update path now needs its parent to exist. A PartiQL SET that previously created an intermediate attribute mid-path is refused, as DynamoDB refuses it.
- All clauses in an expression evaluate against the item's original state, so a single REMOVE can delete two list elements, and a SET that reads an attribute assigned in the same expression still sees the old value.
- A missing attribute is no longer treated as zero. Arithmetic and list_append on an absent or wrongly typed operand now return DynamoDB's error.
Scan validation has also been aligned with the newer DynamoDB behaviour. The author notes that the AWS-side validation rollout first spotted in June has reached 20 of the 33 AWS regions, and Dynoxide's Scan now follows the new error ordering and wording (issue #254).
Two parallel-scan bugs that returned wrong results were fixed. With TotalSegments above 4,096, segments past the last hash bucket returned almost every item; each item now comes back once. Separately, a scan that deleted each page as it went could receive an empty page with no LastEvaluatedKey once its start-key item had been deleted; it now sees its whole segment.
The release also bumps rustls and rustls-webpki in response to published advisories, but the notes say those reach Dynoxide only through test dependencies, so nothing shipped is affected.
What to check before upgrading
The Rust API now validates input the same way the HTTP API does. Database::scan and Database::query reject a Limit of 0, and scan rejects out-of-range Select, ReturnConsumedCapacity and Segment values. An empty UpdateExpression now surfaces as DynoxideError::EnvelopedValidation rather than ValidationException, with the same message. The dynoxide mcp command refuses --max-items 0 at startup, and its scan and query tools reject a limit of 0.
Note the version split: the crate is 3.1.0 while the product release is 1.4.0. Rust users need dynoxide-rs = "3.1" to pick up the new shutdown function, and npm projects can pin the release with npm install --save-dev [email protected].
Why it matters
Local DynamoDB emulation lives or dies on parity: tests that pass against an emulator but fail against the real service — or the reverse — cost teams real debugging time. This release closes a batch of behavioural gaps in areas developers hit constantly, from update expressions to parallel scans and pagination edge cases. The graceful-shutdown API fixes a practical pain point for Rust users running embedded servers in tests, where lingering file handles could block cleanup on Windows. And the .jsonl support, arriving through a community contribution, removes a small but recurring piece of friction for anyone importing DynamoDB exports.
- #dynamodb
- #rust
- #local-testing
- #open-source
- #developer-tools