react-native-map-direction-with-custom-url
v2.0.2
Published
Secure Directions component for react-native-maps that hides Google Maps API Keys using a Backend Proxy to prevent DDoS attacks and quota draining.
Maintainers
Readme
react-native-map-direction-with-custom-url
Directions component for react-native-maps – Draw a route between two coordinates, powered by the Google Maps Directions API.
🛡️ Why use this instead of the standard library?
The standard react-native-maps-directions library requires you to embed your Google Maps API Key directly in your mobile app. This is a security risk as the key can be easily extracted, leading to unauthorized usage or DDoS attacks (exhausting your API quota).
This library innovates by introducing a "Secure Backend Proxy mode":
- Hidden API Keys: Keep your Google Maps keys safe on your server.
- Enhanced Security: Prevent DDoS attacks and unauthorized quota usage.
- Backend Restrictions: Apply custom authentication, rate limiting, and business logic before calling Google.
- Seamless Integration: Your app receives the same high-quality route data as if it called Google directly.
Installation
npm install react-native-map-direction-with-custom-urlor
yarn add react-native-map-direction-with-custom-urlBasic Usage
Mode 1 — Direct Google Maps API
Pass your Google Maps API key directly:
import MapViewDirections from 'react-native-map-direction-with-custom-url';
const origin = {latitude: 37.3318456, longitude: -122.0296002};
const destination = {latitude: 37.771707, longitude: -122.4053769};
const GOOGLE_MAPS_APIKEY = '…';
<MapView initialRegion={…}>
<MapViewDirections
origin={origin}
destination={destination}
apikey={GOOGLE_MAPS_APIKEY}
/>
</MapView>Mode 2 — Secure Backend Proxy (Recommended)
Use this mode to protect your Google Maps API Key. Your app calls a backend endpoint (which you control), and your backend performs the actual Google Maps request. This prevents hackers from stealing your key and allows you to enforce backend-side security.
import MapViewDirections from 'react-native-map-direction-with-custom-url';
<MapView initialRegion={…}>
<MapViewDirections
origin={origin}
destination={destination}
useBackendApi={true}
backendUrl="https://your-api.com/directions"
backendAuthToken="YOUR_BACKEND_AUTH_TOKEN"
/>
</MapView>Your backend must return a response in the Google Maps Directions API format:
{ "status": "OK", "routes": [...] }The component also supports these wrapper formats from your backend:
{ "data": { "status": "OK", "routes": [...] } }
{ "getMapsDirectionsResponse": { "status": "OK", "routes": [...] } }Once the directions between origin and destination have been fetched, a MapView.Polyline between the two will be drawn. Whenever either changes, new directions will be fetched and rendered.
Backend Implementation Guide
To use Mode 2, you need to set up a simple endpoint on your backend.
1. The Endpoint
Your backend should expose a POST endpoint (e.g., /api/directions) that accepts application/x-www-form-urlencoded data.
2. What the Backend calls
When your backend receives a request from the app, it should make a request to the Google Maps Directions API:
GET https://maps.googleapis.com/maps/api/directions/json?origin=...&destination=...&key=YOUR_SECRET_API_KEY
3. Forwarding the Response
Your backend simply needs to forward the exact same JSON response it gets from Google back to the mobile app.
Backend Flow:
- App calls
POST https://your-backend.com/directionswith parameters. - Backend calls
GET https://maps.googleapis.com/maps/api/directions/json?...with your restricted API key stored safely in environment variables. - Backend returns the JSON from Google to the App.
- (Optional) You can add logic to verify the
backendAuthTokenbefore calling Google.
Component API
Props
Required
| Prop | Type | Note |
|---|---|---|
| origin | LatLng or String | The origin location to start routing from. |
| destination | LatLng or String | The destination location to start routing to. |
Mode 1 — Direct Google Maps
| Prop | Type | Default | Note |
|---|---|---|---|
| apikey | String | | Your Google Maps Directions API Key. Required when useBackendApi is false. |
Mode 2 — Backend Proxy
| Prop | Type | Default | Note |
|---|---|---|---|
| useBackendApi | boolean | false | Set to true to route requests through your own backend. |
| backendUrl | String | | Your backend endpoint URL. Required when useBackendApi is true. Your backend must return Google Maps Directions API format. |
| backendAuthToken | String | | Optional Bearer token sent in the Authorization header to authenticate with your backend. |
Shared Props
| Prop | Type | Default | Note |
|---|---|---|---|
| waypoints | [LatLng or String] | [] | Array of waypoints to use between origin and destination. |
| language | String | "en" | The language to use when calculating directions. See here for more info. |
| mode | String | "DRIVING" | Transportation mode. Allowed values: "DRIVING", "BICYCLING", "WALKING", "TRANSIT". |
| resetOnChange | boolean | true | Set to false if you see the directions line glitching when recalculating. |
| optimizeWaypoints | boolean | false | Let Google Maps re-order waypoints for the fastest route. |
| splitWaypoints | boolean | false | Automatically split waypoints into multiple routes to bypass the 10-waypoint API limit. |
| directionsServiceBaseUrl | string | (Google's) | Override the base URL of the Directions API. Usually not needed. |
| region | String | | Region hint for string-based origin/destination to help Google Maps resolve the correct location. |
| precision | String | "low" | Polyline detail level. "low" = smoothed overview path; "high" = full step-by-step path (may hit performance). |
| timePrecision | String | "none" | Set to "now" to get real-time traffic info. |
| channel | String | null | Channel parameter for Google Maps billing analytics. |
More props
Since the result rendered on screen is a MapView.Polyline component, all MapView.Polyline props – except for coordinates – are also accepted.
<MapViewDirections
origin={origin}
destination={destination}
apikey={GOOGLE_MAPS_APIKEY}
strokeWidth={3}
strokeColor="hotpink"
/>An extra note on origin and destination
The values can take several forms:
<MapViewDirections origin={{ latitude: 37.3317876, longitude: -122.0054812 }} destination="…" />
<MapViewDirections origin="37.3317876,-122.0054812" destination="…" />
<MapViewDirections origin="Apple Park Visitor Center" destination="…" />
<MapViewDirections origin="10600 N Tantau Ave, Cupertino, CA 95014, USA" destination="…" />
<MapViewDirections origin="place_id:ChIJW5i0tJC1j4ARoUGtkogTaUU" destination="…" />Events/Callbacks
| Event Name | Returns | Notes |
|---|---|---|
| onStart | { origin, destination, waypoints: [] } | Called when routing has started. |
| onReady | { distance: Number, duration: Number, coordinates: [], fare: Object, waypointOrder: [[]] } | Called when routing successfully finished. Distance in km, duration in minutes. |
| onError | errorMessage | Called when routing has failed. |
Extended Example
import React, { Component } from 'react';
import { Dimensions, StyleSheet } from 'react-native';
import MapView from 'react-native-maps';
import MapViewDirections from 'react-native-map-direction-with-custom-url';
const { width, height } = Dimensions.get('window');
const ASPECT_RATIO = width / height;
const LATITUDE = 37.771707;
const LONGITUDE = -122.4053769;
const LATITUDE_DELTA = 0.0922;
const LONGITUDE_DELTA = LATITUDE_DELTA * ASPECT_RATIO;
// Option A: Direct mode
const GOOGLE_MAPS_APIKEY = '…';
// Option B: Backend proxy mode
const BACKEND_URL = 'https://your-api.com/directions';
const BACKEND_AUTH_TOKEN = '…';
class Example extends Component {
constructor(props) {
super(props);
this.state = {
coordinates: [
{ latitude: 37.3317876, longitude: -122.0054812 },
{ latitude: 37.771707, longitude: -122.4053769 },
],
};
this.mapView = null;
}
onMapPress = (e) => {
this.setState({
coordinates: [...this.state.coordinates, e.nativeEvent.coordinate],
});
}
render() {
return (
<MapView
initialRegion={{
latitude: LATITUDE,
longitude: LONGITUDE,
latitudeDelta: LATITUDE_DELTA,
longitudeDelta: LONGITUDE_DELTA,
}}
style={StyleSheet.absoluteFill}
ref={c => this.mapView = c}
onPress={this.onMapPress}
>
{this.state.coordinates.map((coordinate, index) =>
<MapView.Marker key={`coordinate_${index}`} coordinate={coordinate} />
)}
{(this.state.coordinates.length >= 2) && (
<MapViewDirections
origin={this.state.coordinates[0]}
waypoints={(this.state.coordinates.length > 2) ? this.state.coordinates.slice(1, -1) : undefined}
destination={this.state.coordinates[this.state.coordinates.length - 1]}
// Direct mode:
apikey={GOOGLE_MAPS_APIKEY}
// OR Backend proxy mode:
// useBackendApi={true}
// backendUrl={BACKEND_URL}
// backendAuthToken={BACKEND_AUTH_TOKEN}
strokeWidth={3}
strokeColor="hotpink"
optimizeWaypoints={true}
onStart={(params) => {
console.log(`Started routing between "${params.origin}" and "${params.destination}"`);
}}
onReady={result => {
console.log(`Distance: ${result.distance} km`);
console.log(`Duration: ${result.duration} min.`);
this.mapView.fitToCoordinates(result.coordinates, {
edgePadding: {
right: (width / 20),
bottom: (height / 20),
left: (width / 20),
top: (height / 20),
}
});
}}
onError={(errorMessage) => {
console.log('Routing error:', errorMessage);
}}
/>
)}
</MapView>
);
}
}
export default Example;Changelog
Please see CHANGELOG for more information on what has changed recently.
Credits
- Dhruv Nagvadia (https://github.com/dhruv-nagvadia) — Backend proxy mode, API key restriction support
- Bram(us) Van Damme (https://www.bram.us/) — Original library
- All Contributors
License
The MIT License (MIT). Please see License File for more information.
