Build a Group Decision Micro‑App Using Maps, LLMs and Serverless Functions
tutorialLLMsmaps

Build a Group Decision Micro‑App Using Maps, LLMs and Serverless Functions

ffunctions
2026-01-27
11 min read
Advertisement

Build a real-time group decision micro‑app using LLM prompts, Google Maps traffic ETAs, and serverless functions for fast, low-cost group choices.

Stop the endless group chat ping-pong: build a real-time group decision micro‑app with LLM prompts, live maps and serverless edge LLMs and serverless functions

Problem: You and five friends can’t decide on dinner because everyone suggests different spots and traffic or distance isn’t considered. You want a quick app that aggregates tastes, ranks options with live traffic, and pushes a decision to everyone in real time — without running or maintaining a full backend.

In 2026, this pattern is a quintessential micro app: small, focused, deployable in days, and cheaply operated on serverless platforms and edge runtimes. This tutorial walks through building a group decision micro app — suitable for restaurants, movies, or any choice set — using LLM prompts to normalize preferences, Google Maps (Places + Routes) for live distance and traffic-aware ETAs, and serverless functions + webhooks/websockets for real-time UX.

Executive summary (most important first)

You will learn how to:

  • Collect lightweight user preferences via a micro‑UI or chat widget.
  • Use an LLM to convert textual preferences into numeric preference vectors with explainable scoring.
  • Query Google Maps Places and Routes APIs for candidate locations and traffic-aware ETAs.
  • Rank options by combining LLM-derived preference scores and live travel cost.
  • Implement serverless webhooks and a WebSocket broadcast to deliver real-time updates to the group.
  • Mitigate serverless cold starts, control costs, and design portable functions.

Why this matters in 2026

Late 2025–early 2026 saw three trends that make this micro app both practical and powerful:

  • Open and specialized LLMs matured with instruction-tuned compact models that fit edge and serverless inference cost-effectively; latency and cost per prompt dropped significantly.
  • Cloud providers expanded traffic-aware Routes APIs and low-latency Places lookups, enabling real-time ETA adjustments based on current congestion and incidents.
  • Serverless runtimes added native WebSocket and HTTP/3 support at the edge (AWS Lambda@Edge, Cloudflare Workers, Vercel Edge Functions), making live micro apps feasible without complex socket servers.

Architecture overview

Below is a compact architecture used throughout this guide:

  1. Frontend micro UI (single-page app or chat widget) that collects the group and preferences; opens a WebSocket or SSE connection.
  2. Serverless API endpoints (Node.js/TypeScript) for: creating sessions, recording votes, generating candidate lists (Places API), and ranking (LLM + Maps).
  3. Websocket/Webhook broadcaster: when votes change, a webhook triggers ranking and the serverless function pushes updates to connected clients.
  4. Optional: persistent store (Spreadsheet‑first edge datastores / Supabase) for sessions and caching Places results to reduce API costs.

Key protocols and services: Google Maps (Places + Routes), an LLM endpoint (hosted or cloud), serverless functions (edge or regional), WebSocket provider or platform-native socket, and optionally a database for state/caching.

Step 1 — Define the data model and user flow

Data model (minimal)

  • sessionId: string
  • users: [{ userId, name, location (lat,lng) }]
  • preferences: [{ userId, textPreference ("I want sushi or ramen", rating?) }]
  • candidates: [{ placeId, name, lat, lng, baseScore, etaSeconds, combinedScore }]
  • state: open | ranking | decided

Typical flow

  1. Organizer creates session; shares link.
  2. Participants join and submit short textual preferences.
  3. On each submission, serverless webhook calls ranking function.
  4. Ranking function: fetch candidates (Places), call LLM to convert preferences to scores, compute ETA (Routes), combine scores, sort.
  5. Broadcast sorted list via WebSocket to clients in real time.

Step 2 — LLM prompt design: from text preferences to numeric scores

LLMs are ideal for normalizing heterogeneous inputs ("I'm craving sushi", "I don't want chain restaurants") into a structured score vector. Use an instruction-tuned prompt with a few-shot set to keep outputs deterministic and cost-effective. See prompt templates for inspiration.

Prompt pattern

{
  "system": "You are a preference normalizer. Convert a user's short preference into a JSON object with 'cuisineScores' (map of cuisines to 0-1), 'avoid' (reasons), and 'weight' (0-1 importance). Keep output compact and JSON-only.",
  "user": "Preference: 'Pescatarian, prefer quiet places with outdoor seating'"
}

Example expected response:

{
  "cuisineScores": {"seafood": 0.9, "japanese": 0.6},
  "avoid": ["loud", "buffet"],
  "weight": 0.8
}

In practice, call the LLM once per user submission, cache the parsed object, and combine per-session.

Step 3 — Candidate generation using Google Maps Places

Use the Places API to get a short candidate list (6–12 items) near a computed meeting point (centroid of participants or a chosen anchor). To reduce cost and latency, do a single Nearby Search or Text Search with filters, then a Place Details lookup for each candidate.

// pseudo-Node code to fetch places
const apiKey = process.env.GOOGLE_MAPS_KEY;
const center = `${lat},${lng}`;
const url = `https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=${center}&radius=2500&type=restaurant&key=${apiKey}`;
const res = await fetch(url);
const places = await res.json();

Tip: In 2026, Google added richer filters (noise-level, outdoor seating signals from business metadata). Use them where available to pre-filter candidates server-side and reduce LLM/compute work. Cache results using an edge-friendly datastore like spreadsheet-first edge datastores to cut repeated Maps calls.

Step 4 — ETA and live traffic with Routes API

For each candidate, request the Routes API (or Distance Matrix if you prefer batch calls) to estimate travel time from each participant’s location with current traffic. Normalize ETA into a 0–1 travelScore (lower travel time → higher travelScore).

// compute ETA in seconds (pseudo)
const eta = await getRouteETA(originLatLng, placeLatLng); // uses traffic_enabled=true
const travelScore = 1 / (1 + Math.log(1 + etaSeconds/60));

This formula compresses wide ETA ranges and handles outliers. Adjust to your group's tolerance (longer baseline when friends expect to travel farther).

Step 5 — Combine LLM preference scores with travel scores

Design a scoring function that respects both preference and convenience. A typical linear model:

combinedScore = alpha * preferenceScore + (1 - alpha) * travelScore

Where preferenceScore is the normalized score from the LLM aggregation across participants (weighted by user weight), and alpha (0.5–0.8) prioritizes preference over travel. Expose alpha as a session parameter so groups can opt for "convenience mode". See cost-aware querying patterns when tuning alpha to balance API and compute costs.

Aggregating preferences

  1. Map each user's cuisineScores to candidate cuisines (fuzzy match categories from Places).
  2. Multiply by user's weight and sum across users.
  3. Normalize the result to 0–1 as preferenceScore.

Step 6 — Implement serverless functions

We'll sketch three serverless endpoints (Node.js + Fetch):

  • /session/create — create and return sessionId
  • /vote — accept user preference, call LLM, store parsed pref, trigger ranking
  • /rank — generate candidates, compute ETAs, combine scores, respond with sorted list

Example vote handler (pseudo TypeScript)

import fetch from 'node-fetch';
export async function handler(req) {
  const { sessionId, userId, text } = await req.json();
  // 1. Call LLM to parse
  const llmRes = await fetch(process.env.LLM_ENDPOINT, { method: 'POST', body: JSON.stringify({ prompt: buildPrompt(text) })});
  const parsed = await llmRes.json(); // {cuisineScores, avoid, weight}
  // 2. Store to DB (omitted)
  await db.storePreference(sessionId, userId, parsed);
  // 3. Trigger ranking (async webhook) or call rank directly
  await fetch(`${API_BASE}/rank`, { method: 'POST', body: JSON.stringify({ sessionId }) });
  return new Response(JSON.stringify({ ok: true }), { status: 200 });
}

Example rank function (pseudo)

export async function rankHandler(req) {
  const { sessionId } = await req.json();
  const session = await db.getSession(sessionId);
  const prefs = await db.getPreferences(sessionId);
  const center = computeCentroid(session.users);
  const places = await fetchPlaces(center);
  const candidates = await Promise.all(places.map(async (p) => {
    const etas = await Promise.all(session.users.map(u => getETA(u.location, p.location)));
    const avgEta = average(etas);
    const travelScore = etaToScore(avgEta);
    const prefScore = computePreferenceScore(p, prefs);
    const combined = session.alpha * prefScore + (1 - session.alpha) * travelScore;
    return { ...p, prefScore, travelScore, combined };
  }));
  const sorted = candidates.sort((a,b) => b.combined - a.combined);
  // Broadcast via WebSocket provider
  await broadcaster.broadcast(sessionId, { type: 'ranking', data: sorted });
  return new Response(JSON.stringify(sorted));
}

Step 7 — Real-time UX: WebSockets, SSE, or Webhooks

In 2026, most serverless platforms support WebSocket or native push mechanisms. Choose based on scale and portability:

  • Small groups: use platform WebSocket (Vercel/Cloudflare) or Supabase Realtime for zero-maintenance sockets — see multistream performance patterns.
  • Cross-platform portability: use Webhooks to call a lightweight adapter service that manages socket connections.
  • Fallback: Server-Sent Events (SSE) if you want browser-friendly, one-way updates without socket infrastructure.

Important: keep messages compact (IDs + delta updates). Clients should optimistically update UX while waiting for final ranking to avoid perception of slowness. Tune debouncing and queuing with operational playbooks like cost-aware querying and edge-distribution notes from field reviews.

Step 8 — Cost, latency and reliability considerations

  • LLM cost: call compact instruction-tuned models for parsing (cheap), use larger models only for explainable justification if needed. Consider running parsing on the edge (edge-first model serving).
  • Maps cost: batch Places and Routes calls, cache place details for the session lifetime (15–60 minutes) using an edge or warehouse strategy (cloud data warehouses for long-term analytics).
  • Cold starts: use warmers or prefer edge runtimes (Workers) for the ranking endpoint to keep latency sub-second — see hybrid edge workflows.
  • Concurrency: throttle ranking calls (debounce on frequent votes) and use queueing if many participants update simultaneously. Optimize multistreams with guidance from multistream performance.
  • Observability: emit structured logs and traces (OpenTelemetry) for each ranking call: inputs, LLM token usage, Places count, average ETA, combinedScore stats. Operational playbooks like cost-aware querying and the edge playbook are useful for scaling observability cheaply.

Tuning and advanced strategies

Personalized weights

Allow users to set personal convenience weights; some friends may prefer to drive farther. Store per-user travel tolerance and incorporate it when computing travelScore.

Explainable outputs

Return a short textual justification generated by the LLM for the top 3 choices (e.g., "Sushi House - high seafood match and 10 min away for most people"). Keep it cached and only regenerate when the ranking materially changes. Use cached explanations to control tokens and latency (see cost-aware querying).

Fallback offline mode

If Maps APIs rate-limit or LLM fails, fallback to cached place data and a simple heuristic (distance + user votes) so the app remains responsive. Store short-lived caches in edge-friendly datastores (spreadsheet-first edge datastores).

Privacy and data minimization

Only send minimal location precision if you must (round to 3 decimals ≈ 100m). For privacy-conscious groups, allow approximate location or allow participants to enter a zip code.

Testing and CI/CD

  • Unit test scoring function with edge cases (all users disagree, single-user sessions, far-away anchors).
  • Contract-test the LLM parser by asserting valid JSON and fallback parsing behavior (see prompt templates).
  • Integration test Maps calls using recorded VCR-style fixtures to avoid API costs during CI.
  • Deploy serverless via IaC (Terraform / Pulumi) and use feature flags for alpha parameters like alpha and parsing model. For zero-downtime deployments and safe rollouts, consult release pipeline playbooks.

Example: Putting it together — minimal end-to-end sequence

  1. User A creates session -> /session/create
  2. User B joins, submits "I'm vegetarian and want casual spot" -> /vote
    • vote handler calls LLM -> stores parsed preference
    • vote handler triggers /rank webhook
  3. /rank fetches Places, computes ETAs from each participant, aggregates LLM scores, sorts, broadcasts to WebSocket
  4. Clients receive update and display top 3 with short LLM explanation and a map showing live ETAs

Operational checklist before launch

  • Enable Google Maps billing & restrict API key to your domain.
  • Choose an LLM provider; prefer instruction-tuned compact models for parsing (cost saving).
  • Ensure WebSocket provider can handle low-volume bursts; configure connection timeouts. Consider multistream optimizations from multistream performance.
  • Implement debouncing (200–500ms) for frequent voter updates to reduce redundant rank calls.
  • Implement quotas and graceful degradation when third-party APIs fail; document fallback heuristics and caching in an edge store (edge datastores).

As of early 2026, expect these changes to affect your micro app:

  • More edge LLMs: inference will continue to move closer to the edge, allowing sub-100ms parsing steps without external network calls — see edge-first model serving.
  • Better place metadata: providers will surface dynamic attributes (noise-level, outdoor-seating, outdoor heaters) that improve filtering without LLM guesses.
  • Interoperability standards (CloudEvents & OpenFunction) will simplify cross-provider webhooks and function portability — design your endpoints to accept standard event payloads. Use responsible web data bridge patterns for durable integrations.
  • Billing models: expect more granular function and model pricing; track token/compute usage to avoid surprises (see cost-aware querying).
"Micro apps win when they solve one specific pain quickly with minimal overhead — this pattern for group decisions is a perfect fit."

Actionable takeaways

  • Use a compact LLM model to parse preferences into structured scores and cache outputs to control cost.
  • Generate a short candidate list via Places API, then enrich with Routes ETAs for real-time ranking.
  • Combine preference and travel scores with a tunable alpha; expose that to the group as a mode toggle.
  • Implement ranking in a serverless function that broadcasts via WebSocket or SSE; debounce vote triggers to control concurrency.
  • Instrument everything (token counts, Maps calls, ETA distribution) for predictable cost management — follow cost-aware practices.

Next steps & call to action

Ready to prototype this micro app? Start by scaffolding these files in your repo: a tiny SPA for the UI, three serverless handlers (/session/create, /vote, /rank), and a broadcaster adapter (WebSocket or SSE). If you want a jumpstart, clone a sample repo (search "group-decision-microapp" on GitHub) and replace API keys with your own.

Try this now: scaffold the vote endpoint and test the LLM parsing step with three sample preferences. Verify JSON output and tune your prompt until the parser reliably emits meaningful cuisineScores and weights. Then add Places integration and a dummy travelScore to see live sorting in your UI.

Questions about implementing a specific serverless platform or optimizing prompt cost for your traffic pattern? Ask for a tailored checklist — tell me which platform (Cloudflare, AWS, Vercel, Supabase) and I’ll provide a step-by-step deployment and observability plan. For deployment best practices and zero-downtime releases, consult release pipeline playbooks.

Advertisement

Related Topics

#tutorial#LLMs#maps
f

functions

Contributor

Senior editor and content strategist. Writing about technology, design, and the future of digital media. Follow along for deep dives into the industry's moving parts.

Advertisement
2026-02-04T10:18:40.829Z