Skip to main content

Favorites API Endpoints

The Favorites API allows authenticated users to manage their favorite items. Users can list, add, and remove items from their personal favorites list. Favorite records store item metadata (name, icon, category) for quick display without joining to the items table.

Source directory: template/app/api/favorites/


Authentication

All favorites endpoints require session-based authentication. Unauthenticated requests receive:

Status 401

{
"success": false,
"error": "Unauthorized"
}

List User Favorites

Returns all items favorited by the authenticated user.

PropertyValue
MethodGET
Path/api/favorites
AuthSession (user)
Sourcefavorites/route.ts

Response

Status 200

{
"success": true,
"favorites": [
{
"id": "fav_123abc",
"userId": "user_456def",
"itemSlug": "awesome-productivity-tool",
"itemName": "Awesome Productivity Tool",
"itemIconUrl": "https://example.com/icons/tool.png",
"itemCategory": "productivity",
"createdAt": "2024-01-20T10:30:00.000Z",
"updatedAt": "2024-01-20T10:30:00.000Z"
}
]
}
FieldTypeDescription
favorites[].idstringFavorite record ID
favorites[].userIdstringUser who favorited the item
favorites[].itemSlugstringItem slug identifier
favorites[].itemNamestringItem display name
favorites[].itemIconUrlstring | nullItem icon URL
favorites[].itemCategorystring | nullItem category
favorites[].createdAtstring (ISO 8601)When the item was favorited
favorites[].updatedAtstring | nullLast update timestamp

Favorites are ordered by createdAt (oldest first).

curl Example

curl -s http://localhost:3000/api/favorites \
-H "Cookie: next-auth.session-token=<session_token>"

Add Favorite

Adds an item to the authenticated user's favorites list.

PropertyValue
MethodPOST
Path/api/favorites
AuthSession (user)
Sourcefavorites/route.ts

Request Body

{
"itemSlug": "awesome-productivity-tool",
"itemName": "Awesome Productivity Tool",
"itemIconUrl": "https://example.com/icons/tool.png",
"itemCategory": "productivity"
}
FieldTypeRequiredDescription
itemSlugstringYesUnique item slug identifier (min 1 char)
itemNamestringYesItem display name (min 1 char)
itemIconUrlstringNoItem icon URL
itemCategorystringNoItem category

Responses

Status 201 -- Favorite added successfully.

{
"success": true,
"favorite": {
"id": "fav_123abc",
"userId": "user_456def",
"itemSlug": "awesome-productivity-tool",
"itemName": "Awesome Productivity Tool",
"itemIconUrl": "https://example.com/icons/tool.png",
"itemCategory": "productivity",
"createdAt": "2024-01-20T10:30:00.000Z",
"updatedAt": "2024-01-20T10:30:00.000Z"
}
}

Status 400 -- Invalid request data.

{
"success": false,
"error": "Invalid request data",
"details": "itemSlug is required and must be a non-empty string"
}

Status 409 -- Item already in favorites.

{
"success": false,
"error": "Item is already in favorites"
}

curl Example

curl -s -X POST http://localhost:3000/api/favorites \
-H "Content-Type: application/json" \
-H "Cookie: next-auth.session-token=<session_token>" \
-d '{
"itemSlug": "awesome-productivity-tool",
"itemName": "Awesome Productivity Tool",
"itemIconUrl": "https://example.com/icons/tool.png",
"itemCategory": "productivity"
}'

Remove Favorite

Removes a specific item from the authenticated user's favorites list.

PropertyValue
MethodDELETE
Path/api/favorites/{itemSlug}
AuthSession (user)
Sourcefavorites/[itemSlug]/route.ts

Path Parameters

ParameterTypeDescription
itemSlugstringItem slug identifier to remove from favorites

Responses

Status 200 -- Favorite removed successfully.

{
"success": true,
"message": "Favorite removed successfully"
}

Status 404 -- Favorite not found.

{
"success": false,
"error": "Favorite not found"
}

curl Example

curl -s -X DELETE http://localhost:3000/api/favorites/awesome-productivity-tool \
-H "Cookie: next-auth.session-token=<session_token>"

TypeScript Usage

interface Favorite {
id: string;
userId: string;
itemSlug: string;
itemName: string;
itemIconUrl: string | null;
itemCategory: string | null;
createdAt: string;
updatedAt: string | null;
}

// List all favorites
async function getFavorites(): Promise<Favorite[]> {
const res = await fetch('/api/favorites');
const data = await res.json();
return data.favorites;
}

// Add to favorites
async function addFavorite(item: {
itemSlug: string;
itemName: string;
itemIconUrl?: string;
itemCategory?: string;
}): Promise<Favorite> {
const res = await fetch('/api/favorites', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(item),
});

if (res.status === 409) {
throw new Error('Item is already in favorites');
}

const data = await res.json();
return data.favorite;
}

// Remove from favorites
async function removeFavorite(itemSlug: string): Promise<void> {
const res = await fetch(`/api/favorites/${itemSlug}`, {
method: 'DELETE',
});

if (res.status === 404) {
throw new Error('Favorite not found');
}
}

// Toggle favorite
async function toggleFavorite(
itemSlug: string,
itemName: string,
isFavorited: boolean
): Promise<void> {
if (isFavorited) {
await removeFavorite(itemSlug);
} else {
await addFavorite({ itemSlug, itemName });
}
}

Implementation Notes

  • The favorites table uses a compound uniqueness check on (userId, itemSlug) to prevent duplicates.
  • Item metadata (itemName, itemIconUrl, itemCategory) is stored in the favorites record itself, enabling fast display without additional queries.
  • Deletion checks ownership -- a user can only remove favorites they own.
  • Database availability is checked at the start of each request via checkDatabaseAvailability().
  • Validation errors return Zod error details in the details field.