> ## Documentation Index
> Fetch the complete documentation index at: https://docs.baato.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Search and autocomplete

> Build a search-as-you-type place picker in React Native.

Autocomplete uses two endpoints. [Search](/services/search) returns lightweight suggestions
as the user types; [Places](/services/places) resolves the one they pick into full detail
with coordinates.

<Steps>
  <Step title="Query the Search API">
    ```javascript theme={null}
    import { baatoRequest } from "./baato";

    export function searchPlaces(query, coordinate) {
      const params = { q: query, limit: "20" };

      // Biasing towards the user's location markedly improves ranking
      if (coordinate) {
        params.lat = coordinate.latitude;
        params.lon = coordinate.longitude;
      }

      return baatoRequest("/search", params);
    }
    ```

    <ParamField query="q" type="string" required>
      The search keyword.
    </ParamField>

    <ParamField query="lat" type="number">
      Latitude to bias results towards. Must be sent with `lon`.
    </ParamField>

    <ParamField query="lon" type="number">
      Longitude to bias results towards. Must be sent with `lat`.
    </ParamField>

    <ParamField query="limit" type="integer" default="20">
      Maximum number of suggestions to return.
    </ParamField>
  </Step>

  <Step title="Debounce the input">
    Firing a request per keystroke burns through your quota and produces flickering results
    as responses arrive out of order. 300 ms is a good default.

    ```javascript theme={null}
    import { useEffect, useState } from "react";

    export function useSearch(query, coordinate) {
      const [results, setResults] = useState([]);

      useEffect(() => {
        if (query.length < 2) {
          setResults([]);
          return;
        }

        let cancelled = false;
        const timer = setTimeout(async () => {
          try {
            const data = await searchPlaces(query, coordinate);
            if (!cancelled) setResults(data);
          } catch (error) {
            console.warn("Baato search failed", error);
          }
        }, 300);

        return () => {
          cancelled = true;
          clearTimeout(timer);
        };
      }, [query, coordinate]);

      return results;
    }
    ```

    The `cancelled` flag matters as much as the timer — without it, a slow early request can
    resolve after a fast later one and overwrite fresh results with stale ones.
  </Step>

  <Step title="Render the suggestions">
    ```javascript theme={null}
    <FlatList
      data={results}
      keyExtractor={(item) => String(item.placeId)}
      renderItem={({ item }) => (
        <Pressable onPress={() => handleSelect(item)}>
          <Text style={styles.name}>{item.name}</Text>
          <Text style={styles.address}>{item.address}</Text>
        </Pressable>
      )}
    />
    ```
  </Step>

  <Step title="Resolve the selected place">
    Search results carry no coordinates — that keeps autocomplete responses small. Fetch
    them only for the result the user actually picks.

    ```javascript theme={null}
    export function getPlaceDetails(placeId) {
      return baatoRequest("/places", { placeId });
    }

    async function handleSelect(result) {
      const [place] = await getPlaceDetails(result.placeId);

      camera.current?.setCamera({
        centerCoordinate: [place.centroid.lon, place.centroid.lat],
        zoomLevel: 15,
        animationDuration: 800,
      });
    }
    ```

    <Note>
      `/places` accepts either `placeId` (from a Baato search result) or `osmId` (an
      OpenStreetMap object ID). Both return a one-element array — read `data[0]`.
    </Note>
  </Step>
</Steps>

## Next

<Columns cols={2}>
  <Card title="Reverse geocoding" icon="location-crosshairs" href="/examples/react-native/reverse">
    Go the other way — coordinates to an address.
  </Card>

  <Card title="Search API reference" icon="code" href="/services/search">
    Every parameter, with a live playground.
  </Card>
</Columns>
