Amap
This guide explains how to integrate Amap (高德地图) services into your superun application. The integration uses an edge function to securely retrieve API configuration, then initializes the map in the frontend.
Documentation
For more details about Amap APIs, refer to the official documentation:
Environment Variables
Add the following environment variables to your supabase secrets, the plugin_secret_prefix MUST be SUPERUN
AMAP_WEB_API_KEY - Amap Web API Key
AMAP_JS_API_KEY - Amap JavaScript API Key
AMAP_SECURITY_CODE - Amap Security Code
Important: Never expose these API keys directly in client-side code. Always use an edge function to retrieve the configuration securely.
Step 1: Create Edge Function
Create a Supabase Edge Function to securely return the Amap API configuration:
// supabase/functions/amap-config/index.ts
serve(async (req) => {
// Return Amap configuration
return new Response(
JSON.stringify({
webApiKey: Deno.env.get("SUPERUN_AMAP_WEB_API_KEY"),
jsApiKey: Deno.env.get("SUPERUN_AMAP_JS_API_KEY"),
securityCode: Deno.env.get("SUPERUN_AMAP_SECURITY_CODE"),
}),
{
headers: { "Content-Type": "application/json" },
}
);
});
Step 2: Install Amap Loader
Install the Amap JavaScript API loader:
Step 3: Frontend Integration
import { useEffect, useRef, useState } from "react";
import AMapLoader from "@amap/amap-jsapi-loader";
import { supabase } from "@/integrations/supabase/client";
export default function AmapComponent({
center = [116.397428, 39.90923],
zoom = 13,
mapStyle = "amap://styles/dark",
onMapReady,
onMapError,
className = "h-full w-full",
}: AmapComponentProps) {
const mapContainerRef = useRef<HTMLDivElement>(null);
const mapInstanceRef = useRef<any>(null);
const [isLoading, setIsLoading] = useState(true);
useEffect(() => {
const initMap = async () => {
try {
// Step 1: Fetch API key from edge function
const { data, error } = await supabase.functions.invoke("amap-config");
if (error) throw error;
if (!data?.jsApiKey) {
throw new Error("No API key returned");
}
// Step 2: Configure security code
(window as any)._AMapSecurityConfig = {
securityJsCode: data.securityCode,
};
// Step 3: Load AMap using official loader
setIsLoading(true);
const AMap = await AMapLoader.load({
key: data.jsApiKey, // Use key from Edge Function
version: "2.0",
plugins: [
"AMap.Geolocation",
"AMap.Scale",
"AMap.ToolBar",
"AMap.Marker",
"AMap.InfoWindow",
],
});
// Step 4: Initialize map
if (!mapContainerRef.current) return;
const map = new AMap.Map(mapContainerRef.current, {
zoom,
center,
mapStyle,
viewMode: "3D",
});
mapInstanceRef.current = map;
setIsLoading(false);
// Step 6: Callback with map instance
if (onMapReady) {
onMapReady(map);
}
} catch (error) {
console.error("Failed to initialize Amap:", error);
setIsLoading(false);
if (onMapError) {
onMapError(error as Error);
}
}
};
initMap();
// Cleanup: destroy map on unmount
return () => {
if (mapInstanceRef.current) {
mapInstanceRef.current.destroy();
mapInstanceRef.current = null;
}
};
}, []); // Run once on mount
return (
<div className="relative h-full w-full">
<div ref={mapContainerRef} className={className} />
{/* Loading overlay */}
{isLoading && (
<div className="absolute inset-0 flex items-center justify-center bg-stone-900/90">
<div className="text-center">
<div className="mb-4 h-12 w-12 animate-spin rounded-full border-4 border-stone-700 border-t-amber-500 mx-auto" />
<p className="text-sm text-stone-400">加载地图中...</p>
</div>
</div>
)}
</div>
);
}