Skip to content

Overview

4 min read

Migrating from Node.js to Rust is a strategy problem before it is a coding problem. This section takes the pragmatic path: move incrementally rather than betting the company on a big-bang rewrite, port a Node service one endpoint at a time while keeping its behavior identical, maintain wire-level API compatibility so clients never notice, migrate the data underneath without downtime, measure the performance payoff honestly, and go in clear-eyed about the challenges teams actually hit — including the cases where you should not migrate at all. Every code example is compile-verified against the current stable toolchain (Rust 1.96.0, 2024 edition) and the crate versions it targets (Axum 0.8, Tokio 1, Serde 1, sqlx 0.8, Criterion 0.8).


  • How to apply the strangler-fig pattern to replace a Node.js system slice by slice, with an instant rollback path at every step.
  • How to port an Express endpoint to Axum so that clients cannot tell the implementation changed — same JSON, same status codes, same headers.
  • How to keep the API contract byte-for-byte compatible using Serde attributes (rename_all, skip_serializing_if, custom serializers) and golden-fixture tests.
  • The three data-migration strategies — shared database, dual-write, and backfill — and how to gate a cutover on a reconciliation report instead of optimism.
  • How to measure performance honestly: percentiles over averages, the right unit of work, memory (RSS) as well as latency, and the measurement traps (debug builds, coordinated omission) that produce misleading numbers.
  • The real human and ecosystem challenges: the ownership learning curve, crate-ecosystem gaps, team ramp-up, and a decision framework for when not to migrate.

TopicWhat it covers
Incremental MigrationThe strangler-fig approach: running Node and Rust side by side behind a proxy, going service-by-service, and porting the hot paths first.
Porting a Node.js Service to RustA worked Express-to-Axum walkthrough that keeps observable behavior identical, including the subtle path-parsing differences a naive port gets wrong.
Maintaining API CompatibilityMatching JSON shapes (casing, null-vs-omitted, big integers, dates), status codes, and headers so the wire contract does not drift.
Data Migration StrategiesDatabase migration during a rewrite: shared DB, dual-write, chunked/resumable/idempotent backfill, and a reconciliation gate before cutover.
Measuring Performance Gains HonestlyBenchmarking the right thing: latency percentiles (p50/p99), memory footprint, Criterion and HdrHistogram, and avoiding coordinated omission.
Common Migration ChallengesThe ownership learning curve, ecosystem gaps, team ramp-up, and a framework for deciding when migrating is the wrong call.

By the end of this section you will be able to:

  • Plan a migration by choosing high-value slices (hot paths) instead of easy ones, and sequencing them to ship and roll back independently.
  • Translate an Express handler into an idiomatic Axum handler whose responses are indistinguishable from the original.
  • Pin a wire contract with golden fixtures and contract tests so neither side can break the JSON shape, status codes, or headers by accident.
  • Move live data between a Node-owned and a Rust-owned store using dual-write and backfill, and prove the stores agree before cutting over.
  • Report a defensible performance result with percentiles and memory numbers, stated alongside the conditions that make it reproducible.
  • Make the migrate / don’t-migrate decision on measurable grounds, recognizing the I/O-bound, churning, or ecosystem-gap cases where Rust is the wrong tool.

This section assumes you have worked through the building blocks it ties together:

  • Section 16: Web APIs — Axum routing, extractors, JSON responses, and error handling, which the Express-to-Axum port builds on.
  • Section 17: Databasesqlx, connection pooling, transactions, and schema migrations, which the data-migration strategies rely on.
  • Section 28: Production — configuration, health checks, metrics, graceful shutdown, and tracing, which a migrated service needs to operate safely.

It also leans on the foundations in Section 05: Ownership (the heart of the learning curve), Section 08: Error Handling, Section 11: Async, Section 15: Serialization, and Section 21: Performance.


Approximately 10 hours — roughly 1.5 hours per topic for reading and running the examples, plus the exercises. Budget more if you follow along by standing up a real Node service and porting one endpoint end to end, which is the best way to internalize the material.


Next: Section 30: Projects — apply everything from the guide, including this migration playbook, on full end-to-end builds.