What if you could calculate routes directly in your JavaScript application, without relying on Google Maps, external routing APIs, or a running routing server?
That's the idea behind roadgraph-router, an open-source npm package I've been working on.
After spending time experimenting with OpenStreetMap data, graph structures, and routing algorithms, I wanted to build something that could process real road networks while remaining lightweight enough to run in Node.js or directly in a browser.
Today, I'm releasing the project publicly.
๐ค Why I Built It
OpenStreetMap provides an incredible amount of geographical information, including road networks, intersections, speed limits, and turn restrictions.
But transforming all that information into something you can efficiently use inside a JavaScript application isn't necessarily straightforward.
When working with larger geographical datasets, such as Portugal's road network, I encountered several challenges:
- OpenStreetMap files can be hundreds of megabytes.
- Processing large road networks can require significant memory.
- Loading an entire road network into a browser isn't practical.
- Routing needs to respect one-way streets and turn restrictions.
- I wanted the routing engine to work without depending on an external API.
I started experimenting with ways to compile OSM data into smaller, more efficient graph structures.
That experimentation eventually became roadgraph-router.
๐ Introducing roadgraph-router
roadgraph-router is a TypeScript routing library that converts OpenStreetMap .osm.pbf extracts into compact routing bundles and calculates routes using the A* search algorithm.
The goal is simple: make road routing accessible to JavaScript developers without requiring a dedicated routing service.
Some of its current capabilities include:
-
OpenStreetMap integration: Compile
.osm.pbfextracts from different geographical regions. - A* pathfinding: Calculate road routes between geographical coordinates.
- Compact binary storage: Store road networks in optimized, compressed bundles.
- Partial network loading: Download only the parts of the road network needed for a route.
- Browser and Node.js support: Run routing calculations locally without an external routing API.
- Road snapping: Match geographical coordinates to actual road segments.
- Traffic rule awareness: Respect supported one-way streets, access rules, speed limits, and turn restrictions.
- GeoJSON output: Integrate resulting route geometries into mapping applications.
The initial release focuses on car routing, with opportunities to expand its capabilities over time.
๐ ๏ธ Getting Started
Install the package using npm:
npm install roadgraph-router
Before calculating routes, you'll need to compile an OpenStreetMap extract into a routing bundle.
For example, using a Monaco OSM extract:
npx roadgraph-compile monaco.osm.pbf --output monaco.efrb
The compiler requires Node.js 22.5 or newer.
Once compiled, you can load the routing bundle and calculate a route:
import { RoadRouter } from "roadgraph-router";
const router = await RoadRouter.open(
"/routing/monaco.efrb"
);
const result = await router.route(
{ lat: 43.7384, lng: 7.4246 },
{ lat: 43.7325, lng: 7.4189 }
);
console.log(result.distanceMeters);
console.log(result.durationSeconds);
console.log(result.geometry);
The result contains the calculated distance, estimated duration, and road-following GeoJSON geometry.
This means you can use the output with libraries such as Leaflet or MapLibre to visualize routes on a map.
And because routing happens locally, there's no need to send every route request to an external routing service.
๐ฆ Making Large OSM Files Smaller
One of the most interesting challenges was working with larger OSM extracts.
For example, the Portugal dataset I used for testing was approximately 425 MB.
Rather than loading and maintaining that entire dataset in memory, roadgraph-router compiles the information into a compact, range-addressable binary format.
The results were encouraging.
Portugal compilation benchmark
| Metric | Result |
|---|---|
| Original OSM PBF | 425 MB |
| Compiled routing bundle | 38.03 MB |
| Routing edges | 2,757,265 |
| Compilation time | 7 min 46 s |
| Peak memory usage | 1.11 GB |
That's approximately a 91% reduction in file size compared with the original PBF extract.
Of course, the compiled bundle is a purpose-built routing representation, not a lossless replacement for the original OSM dataset.
The important part is that it retains the road-network information the routing engine needs.
The measurements above were collected on one desktop machine, so actual performance will depend on hardware, geographical data, and compilation options.
๐ You Don't Need to Download the Entire Map
Another important design decision was to avoid downloading the complete routing bundle whenever someone requests a route.
Instead, the package uses HTTP range requests to retrieve the relevant sections of the road network.
For example, in my Portugal benchmark, a short urban route in Lisbon required approximately 4.93 MB of downloaded routing data, despite the complete bundle being 38 MB.
That route took around 118 ms to load its graph data and 86 ms for the A* search, excluding additional overhead.
This approach makes it possible to work with fairly large road networks without loading everything at once.
It is particularly interesting for web applications where bandwidth and memory matter.
๐ง More Than Just Finding the Shortest Path
Finding a path through a graph is one thing.
Finding a usable driving route is another.
Real road networks contain rules that a routing engine needs to understand.
A driver cannot always travel in both directions along a road or turn wherever they want.
roadgraph-router currently handles several important aspects of this problem, including one-way streets, supported turn restrictions, and speed information from OSM.
It also snaps coordinates to road segments rather than simply selecting the closest intersection.
This becomes especially important in rural areas, where junctions can be far apart.
Internally, the routing engine uses A* and can switch to an edge-based search when turn restrictions must be enforced.
There are still limitations. For example, conditional access restrictions, live traffic, and traffic signal delays aren't currently modelled.
I'm treating those as areas for future development rather than pretending the library is already a complete navigation platform.
๐ฎ What's Next?
The package is still in its early stages, and there are several improvements I'd like to work on.
Two of my main priorities are:
1. Different vehicle profiles
Right now, the bundled routing profile focuses on cars.
I'd like to expand this to support other types of vehicles and transportation, each with its own routing rules.
For example, bicycles and pedestrians shouldn't necessarily follow the same road network constraints as cars.
2. Alternative routes
Finding one route is useful, but there are situations where having multiple options is better.
I'd like the engine to eventually return alternative routes when meaningful alternatives exist, allowing applications to compare options by distance or estimated travel time.
Beyond those features, I want to keep improving processing efficiency, bundle sizes, memory consumption, and developer experience.
The objective is to make the package increasingly useful while keeping its architecture understandable and lightweight.
๐ค Open Source and Contributions
roadgraph-router is available on GitHub under the MIT license.
It's a project I'm actively developing, and I'd genuinely appreciate feedback from other developers.
Whether you work with mapping applications, OpenStreetMap, transportation software, routing algorithms, or simply enjoy exploring graph-based problems, I'd be interested in hearing your thoughts.
If you find something that doesn't work as expected, have an idea for an improvement, or want to contribute, feel free to open an issue or pull request.
GitHub: https://github.com/danieljvsa/roadgraph-router
npm: https://www.npmjs.com/package/roadgraph-router
Install it with:
npm install roadgraph-router
๐ฌ Final Thoughts
Building this package has been an interesting exercise in working with large datasets, binary formats, graph algorithms, and geographical information.
There's still a lot I want to improve, but I felt it was time to make it public rather than continue developing it in isolation.
I'm interested in seeing whether other developers find it useful, especially those who want to experiment with routing without operating a dedicated backend service.
If you were building a routing engine in JavaScript, what feature would you prioritize next?
Thanks for reading! ๐
Tags: #javascript #typescript #opensource #showdev
Top comments (1)
Some comments may only be visible to logged-in visitors. Sign in to view all comments.