Skip to main content
Build

Local directory

Wander

Wander is a demo directory built on native geospatial search. Each place stores a real latitude and longitude in a Location field, and a single `$near` filter asks Headless for everything within a radius — returned nearest-first, so the app never sorts by distance itself. Here’s how you’d build your own.

Native geo radius search ($near)
headless.build/demos/demo-local
The Wander demo — a scrollable list of nearby places on the left synced with a Leaflet map of Portland with pins on the right.

Step 0

The content model

Here’s the shape you’re aiming for. You don’t have to build it by hand — the next steps have an agent create it for you — but it helps to see the target first. Expand a type to see its fields.

placeCollection7 fields

A place on the map, with a real geo point.

  • nameText
  • descriptionText
  • locationLocationGEO_POINT { lat, lng } — geo-indexed for $near
  • categoryEnumcoffee · food-cart · park · bookstore · viewpoint
  • ratingNumber
  • pricelevelText$ · $$ · $$$
  • addressText

The recipe

Build your own with an agent

Use this as a blueprint, not a script — adapt the fields and copy to whatever you’re making. Each step is a real prompt you can hand to a coding agent like Claude Code with the Headless MCP server connected; the terminals show the tool calls the agent makes, and the code is what it produces.

  1. Create the project

    The agent creates the project over MCP. Nothing special to configure — the Location field type does the geo work.

    MCP · Headless
    # Spin up the project
    claude "Create a Headless project called Wander for a local places directory"
    create_project { name: "Wander", slug: "demo-local" }
    → demo-local ready
  2. Model the content

    One type is enough. The important field is Location — a native geo point that Headless indexes so it can answer radius queries. Everything else is ordinary metadata.

    MCP · Headless
    # One type, with a real geo point
    claude "Add a Place type with name, description, a Location field, a category enum, rating, price level, and address"
    create_content_type { name: "Place" }
    create_field × 7 (location as a Location field)
    → the Location field is geo-indexed for $near
  3. Seed the places

    Batch-create places, each with real coordinates. Those points are what the `$near` filter searches against.

    MCP · Headless
    # Drop pins around town
    claude "Seed a dozen Portland places across coffee, food carts, parks, bookstores, and viewpoints — each with its real coordinates"
    create_entries place × 12
    publish_entries { count: 12 }
    → every place has a location to search from
  4. Mint a read-only API key

    The map runs in the browser, so create a public, read-only key under Project Settings → API Keys. The `$near` value is `lat,lng,radiusInMeters` — try it against downtown Portland.

    Terminal
    curl -s 'https://headless.build/api/v1/demo-local/place?filters[location][$near]=45.5190,-122.6794,3200' \
      -H "X-API-Key: hls_live_your_read_only_key" | jq
  5. Build the map

    Hand the frontend to the agent — a Leaflet map beside a list. The whole search is one `$near` filter; because Headless returns results ordered nearest-first, the app renders them straight down the list with no distance sorting of its own.

    Claude Code
    # A list synced to a map
    claude "Write a single self-contained index.html with a Leaflet map and a list: send one $near filter for the chosen radius and category, and render the results nearest-first"
    Write public/demos/demo-local/index.html
    → 1 file · one geo filter · sorted by the server
    index.html
    // One $near filter does the radius search — value is "lat,lng,meters".
    function buildQuery(center, radiusMeters, category) {
      const p = new URLSearchParams();
      p.set(
        "filters[location][$near]",
        `${center.lat},${center.lng},${radiusMeters}`,
      );
      if (category !== "all") p.set("filters[category][$eq]", category);
      return `/place?${p}`;
    }
    
    // Headless returns matches ordered nearest-first — no client-side sort.
    const places = (await api(buildQuery(here, 3200, category))).data;
  6. Ship it

    Deploy it anywhere; swap in the visitor’s real coordinates and it becomes a “near me” search. Add a place in Headless and it shows up the next time someone searches its radius — the geo index does the ranking, not your code.

Build your own in an afternoon.

Model your content once, point an agent at it, and ship. Every project speaks REST, GraphQL, and MCP from the first entry.