Backend Integration - Go
Go's standard net/http package handles the proxy with minimal overhead. The only external dependency is gorilla/websocket for the tracking relay.
1
Initialize module
go mod init yourapp
go get github.com/gorilla/websocketSet PICKPOINT_KEY in your shell environment or in a .env file loaded at startup.
2
Helper functions and setup
// main.go
package main
import (
"io"
"log"
"net/http"
"net/url"
"os"
)
const pickpointBase = "https://api.pickpoint.io/v2"
var pickpointKey = os.Getenv("PICKPOINT_KEY")
// proxy forwards a GET request to PickPoint, copying query params.
func proxy(w http.ResponseWriter, r *http.Request, path string) {
target, _ := url.Parse(pickpointBase + path)
target.RawQuery = r.URL.RawQuery
req, _ := http.NewRequestWithContext(r.Context(), "GET", target.String(), nil)
req.Header.Set("X-Api-Key", pickpointKey)
resp, err := http.DefaultClient.Do(req)
if err != nil { http.Error(w, err.Error(), 502); return }
defer resp.Body.Close()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body)
}
// proxyPost forwards a POST request with the request body to PickPoint.
func proxyPost(w http.ResponseWriter, r *http.Request, path string) {
req, _ := http.NewRequestWithContext(r.Context(), "POST", pickpointBase+path, r.Body)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Api-Key", pickpointKey)
resp, err := http.DefaultClient.Do(req)
if err != nil { http.Error(w, err.Error(), 502); return }
defer resp.Body.Close()
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body)
}3
Routes: geocoding, routing, devices
func main() {
mux := http.NewServeMux()
// ── Geocoding ──────────────────────────────────────────────────────────
mux.HandleFunc("GET /geo/forward", func(w http.ResponseWriter, r *http.Request) {
proxy(w, r, "/geocode/forward")
})
mux.HandleFunc("GET /geo/reverse", func(w http.ResponseWriter, r *http.Request) {
proxy(w, r, "/geocode/reverse")
})
mux.HandleFunc("GET /geo/search", func(w http.ResponseWriter, r *http.Request) {
proxy(w, r, "/address/search")
})
// ── Routing ────────────────────────────────────────────────────────────
mux.HandleFunc("POST /route", func(w http.ResponseWriter, r *http.Request) {
proxyPost(w, r, "/route")
})
mux.HandleFunc("POST /route/optimized", func(w http.ResponseWriter, r *http.Request) {
proxyPost(w, r, "/route/optimized")
})
// ── Device Tracking - REST ─────────────────────────────────────────────
mux.HandleFunc("GET /devices", func(w http.ResponseWriter, r *http.Request) {
proxy(w, r, "/devices")
})
mux.HandleFunc("GET /devices/{uid}/tracks", func(w http.ResponseWriter, r *http.Request) {
uid := r.PathValue("uid")
proxy(w, r, "/devices/"+uid+"/tracks")
})
// ── WebSocket relay ────────────────────────────────────────────────────
mux.HandleFunc("GET /tracking-relay", trackingRelay)
log.Println("Listening on :3000")
log.Fatal(http.ListenAndServe(":3000", mux))
}4
WebSocket subscriber relay
// ── WebSocket subscriber relay ─────────────────────────────────────────────
import (
"github.com/gorilla/websocket"
// add to imports above
)
var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
func trackingRelay(w http.ResponseWriter, r *http.Request) {
deviceUID := r.URL.Query().Get("deviceUid")
if deviceUID == "" { http.Error(w, "deviceUid required", 400); return }
// Upgrade client connection
clientConn, err := upgrader.Upgrade(w, r, nil)
if err != nil { return }
defer clientConn.Close()
// Connect to PickPoint as a subscriber
ppURL := "wss://ws.pickpoint.io/v2/tracking/?x-api-key=" + pickpointKey
ppConn, _, err := websocket.DefaultDialer.DialContext(r.Context(), ppURL, nil)
if err != nil { return }
defer ppConn.Close()
// Subscribe
ppConn.WriteJSON(map[string]any{"event": "device:subscribe", "data": map[string]string{"deviceUid": deviceUID}})
// Forward PickPoint messages to client
for {
_, msg, err := ppConn.ReadMessage()
if err != nil { break }
if err := clientConn.WriteMessage(websocket.TextMessage, msg); err != nil { break }
}
}