FilteringQuery Params6 min read

Filtering

Most list endpoints accept query parameters to filter results server-side. This guide covers parameter types, combining filters, and building dynamic filter UIs in React.

Basic filtering

Append query parameters to any list endpoint URL. Multiple parameters are combined with AND logic — all conditions must match.

basic-filters.js
javascript
"color:#c4b5fd">const BASE_URL = "https:">//hotel-data.k3s.ahsm-krakow.com/api/v1";

"color:#6b7280">// Single filter: only enabled properties
"color:#c4b5fd">const properties = "color:#c4b5fd">await fetch(
  "color:#86efac">`${BASE_URL}/properties?enabled=true`
).then((r) => r.json());

"color:#6b7280">// Multiple filters combined: suites with balconies at apt
"color:#c4b5fd">const roomTypes = "color:#c4b5fd">await fetch(
  "color:#86efac">`${BASE_URL}/room-types?property_id=apt&category=suite&has_balcony=true`
).then((r) => r.json());

"color:#6b7280">// Boolean filters: hot tub + duplex + at least 40m2
"color:#c4b5fd">const luxuryRooms = "color:#c4b5fd">await fetch(
  "color:#86efac">`${BASE_URL}/room-types?has_hot_tub=true&is_duplex=true&min_size=40`
).then((r) => r.json());

Filtering issues and maintenance

issues-filters.js
javascript
"color:#6b7280">// Open high-priority issues in room 205
"color:#c4b5fd">const issues = "color:#c4b5fd">await fetch(
  "color:#86efac">`${BASE_URL}/issues?status=open&priority=high&room_number=205&property_id=apt`
).then((r) => r.json());

"color:#6b7280">// All plumbing issues across all properties
"color:#c4b5fd">const plumbingIssues = "color:#c4b5fd">await fetch(
  "color:#86efac">`${BASE_URL}/issues?category=plumbing`
).then((r) => r.json());

"color:#6b7280">// Overdue maintenance schedules
"color:#c4b5fd">const overdue = "color:#c4b5fd">await fetch(
  "color:#86efac">`${BASE_URL}/maintenance/schedules?overdue=true`
).then((r) => r.json());

Building query strings dynamically

Use URLSearchParams to construct query strings from objects. Skip undefined values to avoid sending empty parameters.

query-builder.ts
javascript
"color:#c4b5fd">function buildQueryString(params: Record<string, string | number | boolean | undefined>): string {
  "color:#c4b5fd">const query = "color:#c4b5fd">new URLSearchParams();

  for ("color:#c4b5fd">const [key, value] of Object.entries(params)) {
    "color:#c4b5fd">if (value !== undefined && value !== null && value !== "") {
      query.set(key, String(value));
    }
  }

  "color:#c4b5fd">return query.toString();
}

"color:#6b7280">// Usage
"color:#c4b5fd">const filters = {
  property_id: "apt",
  category: "suite",
  has_balcony: true,
  min_size: 40,
  active: undefined, "color:#6b7280">// skipped
};

"color:#c4b5fd">const qs = buildQueryString(filters);
"color:#6b7280">// → "property_id=apt&category=suite&has_balcony=true&min_size=40"

"color:#c4b5fd">const response = "color:#c4b5fd">await fetch("color:#86efac">`${BASE_URL}/room-types?${qs}`);

Dynamic filter UI in React

Use a custom hook that re-fetches whenever filters change. Serialize the filter object to a stable string for the effect dependency.

useRoomTypes.ts
javascript
"color:#c4b5fd">import { useState, useEffect } "color:#c4b5fd">from "react";

"color:#c4b5fd">const BASE_URL = "https:">//hotel-data.k3s.ahsm-krakow.com/api/v1";

interface RoomTypeFilters {
  property_id?: string;
  category?: string;
  has_hot_tub?: boolean;
  has_balcony?: boolean;
  min_size?: number;
  max_guests?: number;
}

"color:#c4b5fd">function useRoomTypes(filters: RoomTypeFilters) {
  "color:#c4b5fd">const [data, setData] = useState([]);
  "color:#c4b5fd">const [loading, setLoading] = useState(false);
  "color:#c4b5fd">const [error, setError] = useState<Error | null>(null);

  useEffect(() => {
    "color:#c4b5fd">const params = "color:#c4b5fd">new URLSearchParams();
    Object.entries(filters).forEach(([k, v]) => {
      "color:#c4b5fd">if (v !== undefined) params.set(k, String(v));
    });

    setLoading(true);
    fetch("color:#86efac">`${BASE_URL}/room-types?${params}`)
      .then((r) => r.json())
      .then(({ data }) => setData(data))
      ."color:#c4b5fd">catch(setError)
      .finally(() => setLoading(false));
  }, [JSON.stringify(filters)]);

  "color:#c4b5fd">return { data, loading, error };
}

"color:#6b7280">// Usage in a component
"color:#c4b5fd">function RoomTypePicker() {
  "color:#c4b5fd">const [filters, setFilters] = useState<RoomTypeFilters>({
    property_id: "apt",
    active: true,
  });

  "color:#c4b5fd">const { data, loading } = useRoomTypes(filters);

  "color:#c4b5fd">return (
    <div>
      <label>
        <input
          type="checkbox"
          onChange={(e) =>
            setFilters((f) => ({ ...f, has_hot_tub: e.target.checked || undefined }))
          }
        />
        Has hot tub
      </label>
      {/* render data... */}
    </div>
  );
}

Common filter combinations

Reference table of useful filter combinations for common use cases.

GoalEndpointParameters
All active room types at a property/room-types?property_id=apt&active=true
Suites with hot tubs/room-types?category=suite&has_hot_tub=true
Rooms for large groups (4+ guests)/room-types?max_guests=4
Duplex apartments larger than 60m2/room-types?is_duplex=true&min_size=60
All rooms on floor 3 at apt/rooms?property_id=apt&floor=3
Rooms of a specific type/rooms?room_type=apt-penthouse
Open critical issues at krw/issues?property_id=krw&status=open&priority=critical
Overdue maintenance at apt/maintenance/schedules?property_id=apt&overdue=true

Parameter type reference

TypeAccepted valuesExample
stringAny stringproperty_id=apt
booleantrue / falsehas_hot_tub=true
integerWhole numberfloor=3, min_size=40
enumOne of a fixed setstatus=open, priority=high