<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Christian Apeku</title>
    <description>The latest articles on DEV Community by Christian Apeku (@capeku).</description>
    <link>https://dev.to/capeku</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3834142%2F162bd11e-e90f-407e-9727-73bfb038d0d4.png</url>
      <title>DEV Community: Christian Apeku</title>
      <link>https://dev.to/capeku</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/capeku"/>
    <language>en</language>
    <item>
      <title>How to Get Routing, Geocoding and Real-Time Maps With Zero API Keys</title>
      <dc:creator>Christian Apeku</dc:creator>
      <pubDate>Thu, 19 Mar 2026 19:08:51 +0000</pubDate>
      <link>https://dev.to/capeku/how-to-get-routing-geocoding-and-real-time-maps-with-zero-api-keys-5gbc</link>
      <guid>https://dev.to/capeku/how-to-get-routing-geocoding-and-real-time-maps-with-zero-api-keys-5gbc</guid>
      <description>&lt;p&gt;TL;DR: Navigatr is a JavaScript SDK that gives you routing, geocoding, and interactive maps without API keys or billing. It works by making requests directly from users' browsers to public OpenStreetMap infrastructure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It Started With a Tweet&lt;/strong&gt;&lt;br&gt;
Earlier this year, a Chowdeck customer complained on X, they couldn't track their delivery in real time. The replies filled up fast with developers explaining why: &lt;strong&gt;Google Maps API is simply too expensive&lt;/strong&gt; for African startups to run at scale.&lt;br&gt;
I replied with what I knew from having done it before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2id8jg9rl6cfkb26e11.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fc2id8jg9rl6cfkb26e11.jpeg" alt="tweet"&gt;&lt;/a&gt;&lt;br&gt;
The tweet got 5.9K views. People asked the same thing: is there a package for this?&lt;br&gt;
There wasn't. So I &lt;a href="https://www.navigatr.dev/" rel="noopener noreferrer"&gt;built one&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gx3o20i2t7btiyge93c.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7gx3o20i2t7btiyge93c.png" alt="navigatr home screen"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Problem&lt;/strong&gt;&lt;br&gt;
If you've ever built a location-based app, you know the drill:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Sign up for Google Maps&lt;/li&gt;
&lt;li&gt;Add a credit card&lt;/li&gt;
&lt;li&gt;Watch your bill grow with every user&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Google Maps charges $7 per 1,000 routes and $5 per 1,000 geocodes. For a delivery app doing 100K orders/month, that's $1,200/month minimum just for maps.&lt;br&gt;
For a Lagos or Accra startup still finding product market fit, that's not a maps bill. That's a hiring decision. A runway decision.&lt;br&gt;
And then there's the rate limiting, the ToS changes, and the vendor lock-in.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Idea&lt;/strong&gt;&lt;br&gt;
What if we could use the same underlying data Google uses OpenStreetMap but skip the middleman entirely?&lt;br&gt;
OSM has free, public APIs:&lt;/p&gt;

&lt;p&gt;Valhalla for routing (turn-by-turn directions, ETAs)&lt;br&gt;
Nominatim for geocoding (address → coordinates)&lt;br&gt;
Photon for autocomplete&lt;br&gt;
OpenFreeMap for map tiles&lt;/p&gt;

&lt;p&gt;The catch? These are meant for distributed use. You can't hammer them from a single server IP.&lt;br&gt;
But what if every request came from a different IP?&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Architecture&lt;/strong&gt;&lt;br&gt;
Navigatr is a client-side SDK. When your user needs a route, the request goes directly from their browser to the public infrastructure not through your backend.&lt;/p&gt;

&lt;p&gt;Traditional (Google Maps):&lt;br&gt;
User → Your Server → Google → $$$ Bill&lt;/p&gt;

&lt;p&gt;Navigatr (Distributed):&lt;br&gt;
User A → Public APIs&lt;br&gt;
User B → Public APIs&lt;br&gt;
User C → Public APIs&lt;/p&gt;

&lt;p&gt;With 10,000 users, you have 10,000 different IPs making requests. Each user has their own rate limit quota. Your server never touches the maps API.&lt;br&gt;
Zero infrastructure. Zero cost. Zero API keys.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Code&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;shell
npm &lt;span class="nb"&gt;install&lt;/span&gt; @navigatr/web
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Navigatr&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@navigatr/web&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nav&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Navigatr&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;container&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;map&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;center&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;lat&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;5.6037&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lng&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mf"&gt;0.1870&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;geocode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Accra Mall, Ghana&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;geocode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;address&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Kotoka Airport, Ghana&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;route&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;route&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;origin&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;destination&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;drawRoute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;polyline&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;durationText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// "12 mins"&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;route&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;distanceText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// "8.2 km"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No API key. No billing. Just maps.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Built for Delivery &amp;amp; Ride-Sharing&lt;/strong&gt;&lt;br&gt;
Navigatr includes first-class support for the workflows that sparked it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ride&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;nav&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;createRide&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;pickup&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;riderLocation&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;airportCoords&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;onETAUpdate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eta&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;phase&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;showETA&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;eta&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;durationText&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startPickup&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;driverLocation&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nx"&gt;websocket&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;driver-location&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;updateDriverLocation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startTrip&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="nx"&gt;ride&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;complete&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The SDK handles real-time ETA updates, route recalculation, driver marker animation, and camera-following navigation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What About Scale?&lt;/strong&gt;&lt;br&gt;
For most apps, the public infrastructure works fine. When you outgrow it:&lt;/p&gt;

&lt;p&gt;Self-host Docker Compose setup with all services bundled&lt;br&gt;
Navigatr Cloud (coming soon) — Managed infrastructure, capacity based pricing&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;javascript&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;nav&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Navigatr&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;serverUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://maps.yourcompany.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same SDK, different backend. No code changes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Honest Limitations&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;No live traffic data — ETAs are based on speed limits, not current conditions&lt;/li&gt;
&lt;li&gt;No Street View&lt;/li&gt;
&lt;li&gt;No rich POI data — limited business info vs Google Places&lt;/li&gt;
&lt;li&gt;For core use cases — routing, geocoding, real-time tracking &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Navigatr works. And it costs nothing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why Open Source?&lt;/strong&gt;&lt;br&gt;
Because developers shouldn't have to choose between paying Google $10K/month or building maps infrastructure from scratch.&lt;br&gt;
The SDK is MIT licensed. Fork it, self-host it, contribute to it.&lt;br&gt;
&lt;a href="https://www.npmjs.com/package/navigatr" rel="noopener noreferrer"&gt;npm&lt;/a&gt; · &lt;a href="http://github.com/capeku/navigatr" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; · &lt;a href="//navigatr.dev/api"&gt;Docs&lt;/a&gt; &lt;/p&gt;

&lt;p&gt;Built on Valhalla, Nominatim, Photon, MapLibre GL JS, and OpenStreetMap.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>map</category>
      <category>npm</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
