Go Quickstart
Server-side use: tile proxy, batch geocoding, downloads.
Tile proxy (net/http)
go
package main
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"fmt"
"io"
"log"
"net/http"
"os"
"strconv"
"time"
)
var (
key = os.Getenv("MFD_SRV_KEY")
secret = []byte(os.Getenv("MFD_SRV_SECRET"))
)
func signHeaders(method, path string) (string, string) {
ts := strconv.FormatInt(time.Now().Unix(), 10)
mac := hmac.New(sha256.New, secret)
fmt.Fprintf(mac, "%s\n%s\n%s", method, path, ts)
return ts, hex.EncodeToString(mac.Sum(nil))
}
func tileHandler(w http.ResponseWriter, r *http.Request) {
path := r.URL.Path
ts, sig := signHeaders("GET", path)
req, _ := http.NewRequest("GET", "https://tiles.mapsfordevs.com"+path, nil)
req.Header.Set("Authorization", "Bearer "+key)
req.Header.Set("X-MFD-Timestamp", ts)
req.Header.Set("X-MFD-Signature", sig)
resp, err := http.DefaultClient.Do(req)
if err != nil {
http.Error(w, "upstream error", http.StatusBadGateway)
return
}
defer resp.Body.Close()
w.Header().Set("Content-Type", "application/x-protobuf")
w.Header().Set("Cache-Control", "public, max-age=86400")
w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body)
}
func main() {
http.HandleFunc("/tiles/", tileHandler)
log.Fatal(http.ListenAndServe(":8080", nil))
}Geocoding client
go
package mfd
import (
"encoding/json"
"fmt"
"net/http"
"net/url"
)
type GeocodeItem struct {
Lat float64 `json:"lat"`
Lng float64 `json:"lng"`
Label string `json:"label"`
Components map[string]string `json:"components"`
Confidence float64 `json:"confidence"`
Type string `json:"type"`
}
type geocodeResp struct {
OK bool `json:"ok"`
Items []GeocodeItem `json:"items"`
Error string `json:"error"`
}
func Geocode(apiKey, query, country string, limit int) ([]GeocodeItem, error) {
q := url.Values{}
q.Set("q", query)
q.Set("limit", fmt.Sprintf("%d", limit))
if country != "" {
q.Set("country", country)
}
req, _ := http.NewRequest("GET",
"https://api.mapsfordevs.com/geocode/forward?"+q.Encode(), nil)
req.Header.Set("Authorization", "Bearer "+apiKey)
resp, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
var body geocodeResp
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
return nil, err
}
if !body.OK {
return nil, fmt.Errorf("mfd: %s", body.Error)
}
return body.Items, nil
}Retry with backoff
go
func fetchWithRetry(req *http.Request, attempts int) (*http.Response, error) {
for i := 0; i < attempts; i++ {
resp, err := http.DefaultClient.Do(req)
if err == nil && resp.StatusCode < 500 && resp.StatusCode != 429 {
return resp, nil
}
var delay time.Duration
if resp != nil {
if ra := resp.Header.Get("Retry-After"); ra != "" {
if s, err := strconv.Atoi(ra); err == nil {
delay = time.Duration(s) * time.Second
}
}
resp.Body.Close()
}
if delay == 0 {
delay = time.Duration(1<<i) * time.Second
}
time.Sleep(delay)
}
return nil, fmt.Errorf("failed after %d attempts", attempts)
}