Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/astrxnomo/tourify/llms.txt

Use this file to discover all available pages before exploring further.

Overview

Favorites in Tourify are polymorphic — a single favorites table stores bookmarks for any content type using the favorable_type and favorable_id columns. The same API endpoints handle places, events, and cities uniformly.
All favorites endpoints require authentication. Include the Authorization: Bearer {your-token} header on every request.
Supported favorable_type values:
ValueTarget model
placeA tourist place
eventA scheduled event
cityA city
The database prevents duplicate favorites through firstOrCreate on the (user_id, favorable_id, favorable_type) combination.

Endpoints

GET /api/favorites

Returns all favorites belonging to the authenticated user. Each favorite includes the full related item (favorable) with its images loaded.
curl -s http://localhost:8000/api/favorites \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 15|xYzAbCdEfGhIjKlMnOpQrStUvWx0987654321"

POST /api/favorites

Add an item to the authenticated user’s favorites. If the item is already favorited, the existing record is returned (no duplicate is created). Required fields
favorable_id
integer
required
The primary key of the item to bookmark (e.g. 10 for place with ID 10).
favorable_type
string
required
The type of item. Must be one of place, event, or city.
curl -s -X POST http://localhost:8000/api/favorites \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 15|xYzAbCdEfGhIjKlMnOpQrStUvWx0987654321" \
  -d '{
    "favorable_id": 10,
    "favorable_type": "place"
  }'

DELETE /api/favorites/

Remove a favorite by its own ID. The server checks that the favorite belongs to the authenticated user and returns 403 if it does not.
curl -s -X DELETE http://localhost:8000/api/favorites/55 \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 15|xYzAbCdEfGhIjKlMnOpQrStUvWx0987654321"

DELETE /api/favorites

Remove a favorite by its morph pair (favorable_id + favorable_type) instead of by its own ID. This is the preferred method in the mobile app because the frontend tracks favorites by type and item ID rather than storing the favorite.id. Required fields
favorable_id
integer
required
The primary key of the item to un-bookmark.
favorable_type
string
required
The type of the item. Must be one of place, event, or city.
curl -s -X DELETE http://localhost:8000/api/favorites \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  -H "Authorization: Bearer 15|xYzAbCdEfGhIjKlMnOpQrStUvWx0987654321" \
  -d '{
    "favorable_id": 10,
    "favorable_type": "place"
  }'

Frontend integration

The useFavorites hook manages the favorites list in the React Native app. It provides isFavorite(type, id) to check whether an item is bookmarked and toggleFavorite(type, id) to add or remove it atomically. The morph-based delete (DELETE /api/favorites with a body) is used to avoid storing the favorite.id in client state.
hooks/useFavorites.js
import { useCallback, useEffect, useState } from "react";
import { del, get, post } from "../services/post";

export function useFavorites() {
  const [favorites, setFavorites] = useState([]);
  const [loading, setLoading] = useState(true);

  const load = useCallback(async () => {
    setLoading(true);
    try {
      setFavorites(await get("/favorites"));
    } catch {
      setFavorites([]);
    } finally {
      setLoading(false);
    }
  }, []);

  useEffect(() => { load(); }, [load]);

  // Check if an item is already favorited
  const isFavorite = useCallback(
    (type, id) =>
      favorites.some((f) => f.favorable_type === type && f.favorable_id === id),
    [favorites],
  );

  // Add or remove a favorite based on current state
  const toggleFavorite = useCallback(
    async (type, id) => {
      if (isFavorite(type, id)) {
        // Uses morph-based DELETE — no need to know the favorite's own ID
        await del("/favorites", { favorable_type: type, favorable_id: id });
        setFavorites((prev) =>
          prev.filter(
            (f) => !(f.favorable_type === type && f.favorable_id === id),
          ),
        );
      } else {
        const newFav = await post("/favorites", {
          favorable_type: type,
          favorable_id: id,
        });
        setFavorites((prev) => [
          ...prev,
          { ...newFav, favorable_type: type, favorable_id: id },
        ]);
      }
    },
    [isFavorite],
  );

  return { favorites, loading, refresh: load, isFavorite, toggleFavorite };
}
Usage example in a screen:
PlaceDetailScreen.jsx (excerpt)
const { isFavorite, toggleFavorite } = useFavorites();

const favorited = isFavorite("place", place.id);

<TouchableOpacity onPress={() => toggleFavorite("place", place.id)}>
  <Ionicons
    name={favorited ? "heart" : "heart-outline"}
    size={24}
    color={favorited ? "#e74c3c" : "#999"}
  />
</TouchableOpacity>

Build docs developers (and LLMs) love