Radar
"Radar: geofencing, geocoding, trip tracking, address validation, maps, fraud detection, JavaScript SDK"
Radar is a location infrastructure platform that treats geolocation as a first-class backend concern rather than a purely client-side rendering problem. While traditional mapping libraries focus on visual display, Radar provides server-side primitives -- geofences, trip tracking, address validation, IP geocoding, and fraud detection -- that power location-aware business logic. The platform exposes both a client-side JavaScript SDK for browser and mobile contexts and a server-side REST API for backend workflows. Use Radar when your product needs to answer "where is this user and what does that mean for our business" rather than just "show a map." Radar's Maps product provides a cost-effective alternative to Google Maps for display purposes, but its real differentiator is the geofencing and intelligence layer. ## Key Points - **Use publishable keys client-side, secret keys server-side**: Publishable keys are safe for browsers but have limited scope. Never expose secret keys in frontend bundles. - **Set userId before tracking**: All geofence events, trip updates, and analytics are associated with the userId. Set it immediately after authentication. - **Use webhook integrations for real-time events**: Configure Radar webhooks to push geofence entry/exit events to your backend rather than polling the API. - **Batch geocoding server-side**: For bulk address validation (e.g., importing a CSV of customer addresses), use the server API with rate limiting rather than client-side calls. - **Combine IP geocoding with device GPS**: Use IP geocoding as a coarse fallback and fraud signal. It is not accurate enough for geofencing or navigation. - **Tag geofences by category**: Use the `tag` field (e.g., "store", "warehouse", "delivery-zone") to organize geofences and filter events by business domain. - **Monitor trip ETAs via webhooks**: Subscribe to `trip.updated` webhook events to push real-time ETA updates to your customers without polling. - **Ignoring geofence event deduplication**: Radar can fire the same geofence entry event if the user's location jitters at a boundary. Deduplicate events by `event._id` in your webhook handler. - **Creating geofences client-side**: The client SDK cannot create geofences. Always create and manage geofences through the server API or the Radar dashboard.
skilldb get maps-geolocation-services-skills/RadarFull skill: 314 linesRadar
Core Philosophy
Radar is a location infrastructure platform that treats geolocation as a first-class backend concern rather than a purely client-side rendering problem. While traditional mapping libraries focus on visual display, Radar provides server-side primitives -- geofences, trip tracking, address validation, IP geocoding, and fraud detection -- that power location-aware business logic. The platform exposes both a client-side JavaScript SDK for browser and mobile contexts and a server-side REST API for backend workflows. Use Radar when your product needs to answer "where is this user and what does that mean for our business" rather than just "show a map." Radar's Maps product provides a cost-effective alternative to Google Maps for display purposes, but its real differentiator is the geofencing and intelligence layer.
Setup
Install the SDK and initialize:
// npm install radar-sdk-js
import Radar from "radar-sdk-js";
import "radar-sdk-js/dist/radar.css"; // only needed if using Radar Maps
// Initialize with your publishable key
Radar.initialize(process.env.NEXT_PUBLIC_RADAR_PUBLISHABLE_KEY!);
For server-side calls, use the REST API directly:
const RADAR_SECRET_KEY = process.env.RADAR_SECRET_KEY!;
async function radarApi<T>(
endpoint: string,
params: Record<string, string> = {}
): Promise<T> {
const url = new URL(`https://api.radar.io/v1${endpoint}`);
Object.entries(params).forEach(([k, v]) => url.searchParams.set(k, v));
const res = await fetch(url, {
headers: { Authorization: RADAR_SECRET_KEY },
});
if (!res.ok) {
const error = await res.json();
throw new Error(`Radar API error: ${error.meta?.message ?? res.statusText}`);
}
return res.json() as Promise<T>;
}
Key Techniques
Forward and Reverse Geocoding
interface RadarAddress {
formattedAddress: string;
latitude: number;
longitude: number;
country: string;
state: string;
city: string;
postalCode: string;
confidence: "exact" | "interpolated" | "fallback";
}
// Client-side forward geocode
async function geocodeAddress(query: string): Promise<RadarAddress[]> {
const result = await Radar.forwardGeocode({ query });
return result.addresses;
}
// Server-side reverse geocode
async function reverseGeocode(lat: number, lng: number): Promise<RadarAddress> {
const data = await radarApi<{ addresses: RadarAddress[] }>("/geocode/reverse", {
coordinates: `${lat},${lng}`,
});
return data.addresses[0];
}
Address Validation and Autocomplete
// Autocomplete for search inputs
async function autocompleteAddress(
query: string,
near?: { latitude: number; longitude: number }
): Promise<RadarAddress[]> {
const result = await Radar.autocomplete({
query,
near: near ? near : undefined,
limit: 5,
layers: ["address", "place"],
});
return result.addresses;
}
// Server-side address validation
interface ValidationResult {
address: RadarAddress;
verificationStatus: "verified" | "unverified" | "ambiguous";
result: {
deliverability: "deliverable" | "undeliverable" | "unknown";
};
}
async function validateAddress(addressString: string): Promise<ValidationResult> {
const data = await radarApi<{ result: ValidationResult }>("/addresses/validate", {
address: addressString,
});
return data.result;
}
Geofencing
// Create a geofence via the server API
interface GeofenceParams {
tag: string;
externalId: string;
description: string;
type: "circle" | "polygon";
coordinates: [number, number]; // [lng, lat] for circle center
radius: number; // meters, for circle type
metadata?: Record<string, string>;
}
async function createGeofence(params: GeofenceParams): Promise<void> {
await fetch("https://api.radar.io/v1/geofences", {
method: "POST",
headers: {
Authorization: RADAR_SECRET_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
tag: params.tag,
externalId: params.externalId,
description: params.description,
type: params.type,
coordinates: params.coordinates,
radius: params.radius,
metadata: params.metadata,
}),
});
}
// Client-side: track user and detect geofence events
async function startGeofenceTracking(): Promise<void> {
// Identify the user first
Radar.setUserId("user-123");
Radar.setMetadata({ plan: "premium" });
// Single location check
const { events, location, user } = await Radar.trackOnce();
// events contains geofence entries/exits
events.forEach((event) => {
if (event.type === "user.entered_geofence") {
console.log(`Entered: ${event.geofence?.description}`);
}
if (event.type === "user.exited_geofence") {
console.log(`Exited: ${event.geofence?.description}`);
}
});
}
Trip Tracking
interface TripOptions {
externalId: string;
destinationGeofenceTag: string;
destinationGeofenceExternalId: string;
mode: "car" | "foot" | "bike";
}
// Start tracking a trip (e.g., delivery driver en route)
async function startTrip(options: TripOptions): Promise<void> {
Radar.setUserId("driver-456");
await Radar.startTrip({
tripOptions: {
externalId: options.externalId,
destinationGeofenceTag: options.destinationGeofenceTag,
destinationGeofenceExternalId: options.destinationGeofenceExternalId,
mode: options.mode,
},
trackingOptions: {
desiredStoppedUpdateInterval: 30,
desiredMovingUpdateInterval: 15,
desiredSyncInterval: 10,
desiredAccuracy: "high",
stopDuration: 60,
stopDistance: 50,
sync: "all",
replay: "stops",
showBlueBar: true,
},
});
}
// Query trip status server-side
interface TripStatus {
externalId: string;
status: "started" | "approaching" | "arrived" | "completed" | "canceled";
eta: { duration: number; distance: number };
}
async function getTripStatus(tripExternalId: string): Promise<TripStatus> {
const data = await radarApi<{ trip: TripStatus }>(`/trips/${tripExternalId}`);
return data.trip;
}
// Complete or cancel
async function completeTrip(): Promise<void> {
await Radar.completeTrip();
Radar.stopTracking();
}
Fraud Detection with IP Geocoding
interface IpGeocodeResult {
ip: string;
city: string;
region: string;
country: string;
latitude: number;
longitude: number;
proxy: boolean;
vpn: boolean;
tor: boolean;
datacenter: boolean;
}
async function checkIpFraudSignals(ip: string): Promise<{
location: IpGeocodeResult;
riskFlags: string[];
}> {
const data = await radarApi<{ address: IpGeocodeResult }>("/geocode/ip", { ip });
const location = data.address;
const riskFlags: string[] = [];
if (location.proxy) riskFlags.push("proxy_detected");
if (location.vpn) riskFlags.push("vpn_detected");
if (location.tor) riskFlags.push("tor_exit_node");
if (location.datacenter) riskFlags.push("datacenter_ip");
return { location, riskFlags };
}
Radar Maps (Display)
import Radar from "radar-sdk-js";
function initRadarMap(containerId: string) {
const map = Radar.ui.map({
container: containerId,
style: "radar-default-v1",
center: [-73.9857, 40.7484],
zoom: 12,
});
// Add a marker
Radar.ui.marker({ text: "HQ" })
.setLngLat([-73.9857, 40.7484])
.addTo(map);
// The map instance is Maplibre GL JS under the hood
map.on("load", () => {
map.addSource("deliveries", {
type: "geojson",
data: { type: "FeatureCollection", features: [] },
});
map.addLayer({
id: "delivery-points",
type: "circle",
source: "deliveries",
paint: {
"circle-radius": 6,
"circle-color": "#6366f1",
},
});
});
return map;
}
Best Practices
- Use publishable keys client-side, secret keys server-side: Publishable keys are safe for browsers but have limited scope. Never expose secret keys in frontend bundles.
- Set userId before tracking: All geofence events, trip updates, and analytics are associated with the userId. Set it immediately after authentication.
- Use webhook integrations for real-time events: Configure Radar webhooks to push geofence entry/exit events to your backend rather than polling the API.
- Batch geocoding server-side: For bulk address validation (e.g., importing a CSV of customer addresses), use the server API with rate limiting rather than client-side calls.
- Combine IP geocoding with device GPS: Use IP geocoding as a coarse fallback and fraud signal. It is not accurate enough for geofencing or navigation.
- Tag geofences by category: Use the
tagfield (e.g., "store", "warehouse", "delivery-zone") to organize geofences and filter events by business domain. - Monitor trip ETAs via webhooks: Subscribe to
trip.updatedwebhook events to push real-time ETA updates to your customers without polling.
Anti-Patterns
- Using Radar solely as a map renderer: Radar Maps is built on MapLibre GL and is cost-effective, but the platform's value is in geofencing, tracking, and intelligence. If you only need map display, a free tile provider with Leaflet or MapLibre may be simpler.
- Polling trackOnce in a tight loop:
Radar.trackOnce()is for single location checks. For continuous tracking, useRadar.startTrip()orRadar.trackContinuous()which handle battery-efficient background updates. - Ignoring geofence event deduplication: Radar can fire the same geofence entry event if the user's location jitters at a boundary. Deduplicate events by
event._idin your webhook handler. - Creating geofences client-side: The client SDK cannot create geofences. Always create and manage geofences through the server API or the Radar dashboard.
- Validating addresses without handling ambiguous results: The validation API returns
ambiguouswhen multiple matches exist. Always present disambiguation options to the user rather than silently picking the first result. - Skipping metadata on users and geofences: Metadata fields let you attach business context (plan tier, store ID, driver shift) that flows through to webhook payloads, making event processing much easier downstream.
Install this skill directly: skilldb add maps-geolocation-services-skills
Related Skills
Google Maps Platform
"Google Maps Platform: Maps JavaScript API, Places, Geocoding, Directions, Street View, React (@vis.gl/react-google-maps)"
HERE Maps
HERE Maps Platform: Maps API for JavaScript, geocoding, routing, isoline routing, fleet telematics, and platform data services
Leaflet
"Leaflet: open-source maps, markers, popups, layers, GeoJSON, plugins, React (react-leaflet), tile providers"
Mapbox
"Mapbox: interactive maps, GL JS, geocoding, directions, markers, layers, 3D terrain, React (react-map-gl)"
OpenLayers
OpenLayers: high-performance open-source map library with vector tiles, projections, OGC services (WMS/WFS), drawing interactions, and GeoJSON/KML support
OpenStreetMap & Nominatim
OpenStreetMap tile usage and Nominatim geocoding API: forward/reverse geocoding, tile servers, Overpass API queries, and attribution requirements