Skip to main content

Saved Posts & Collections — Technical Reference

← Back to Saved Posts & Collections

Where this lives

Backend

Frontend

Technical implementation checklist

  • savePost / unsavePost — resolvers wired; PostCard.tsx's bookmark icon is a real working toggle
  • myCollections / collectionPosts — wired; consumed by SavedCollectionsSection.tsx under the Saved tab. There is no more all-collections-at-once query (mySavedPosts was removed); the UI drills into one collection at a time.
  • createCollection / deleteCollection — wired; consumed by SavedCollectionsSection.tsx / SaveToCollectionModal.tsx
  • updateCollection (rename) — wired; SavedCollectionsSection.tsx's handleRename calls it from the collection detail view
  • toggleCollectionPrivacy — wired; SavedCollectionsSection.tsx's handleTogglePrivacy (Public/Private pill in the collection detail view)
  • movePostToCollection — wired; SavedCollectionsSection.tsx's handleMovePost (hover a post → "Move" → pick target)
  • publicCollections — wired; DiscoverCollectionsPage.tsx at /settings/discover-collections

Collections (SavedCollection)

FieldDescription
nameCollection name
descriptionOptional description
isPublicVisible to other users
isDefaultAuto-created "Saved" collection every user gets on first save. deleteCollection rejects deleting it (backend-enforced); SavedCollectionsSection.tsx also hides the rename/privacy/delete controls for it in the UI. The frontend displays it under a localized label (collections.defaultName) instead of its raw name
postCountNumber of saved posts

myCollections returns all collections owned by the authenticated user, including the postCount to render the grid thumbnail count. It also lazily creates the user's default collection first, so it's always present even before their first save. publicCollections returns a discoverable feed of public collections from any user.

createCollection creates a new named collection. updateCollection changes the name or description. deleteCollection removes it and unsaves all posts inside it (posts are not deleted); it rejects deleting the default (isDefault) collection. toggleCollectionPrivacy flips between public and private in a single call.

query MyCollections { myCollections { id name description isPublic isDefault postCount } }
query PublicCollections($limit: Int) { publicCollections(limit: $limit) { id name user { username } } }

mutation CreateCollection($input: SavedCollectionCreateInput!) { createCollection(input: $input) { id name } }
mutation UpdateCollection($collectionId: ID!, $input: SavedCollectionUpdateInput!) {
updateCollection(collectionId: $collectionId, input: $input) { id name }
}
mutation DeleteCollection($collectionId: ID!) { deleteCollection(collectionId: $collectionId) }
mutation ToggleCollectionPrivacy($collectionId: ID!) { toggleCollectionPrivacy(collectionId: $collectionId) { isPublic } }

Saved posts (SavedPost)

collectionPosts returns the saved posts in one specific collection (collectionId is required). There is no more single query that returns saved posts across every collection at once — the frontend lists collections via myCollections and drills into one via collectionPosts per collection.

isPostSaved is a lightweight boolean check — use it to render the bookmark icon state on a post card. savedPostsCount returns a single integer for the profile stats row.

savePost saves a post, optionally into a collection. If collectionId is omitted: re-saving an already-saved post leaves it in its current collection unchanged, while a brand-new save is placed into the user's auto-created default ("Saved") collection — there's no longer a collection-less/"Uncollected" state. unsavePost removes it from whichever collection it's in. movePostToCollection changes which collection a previously saved post belongs to.

query CollectionPosts($collectionId: ID!, $limit: Int, $offset: Int) {
collectionPosts(collectionId: $collectionId, limit: $limit, offset: $offset) {
id
post { id text media { mediaUrl } }
collection { name }
createdAt
}
}

query IsPostSaved($postId: ID!) { isPostSaved(postId: $postId) }
query SavedPostsCount { savedPostsCount }

mutation SavePost($postId: ID!, $collectionId: ID) { savePost(postId: $postId, collectionId: $collectionId) { id } }
mutation UnsavePost($postId: ID!) { unsavePost(postId: $postId) }
mutation MovePostToCollection($postId: ID!, $collectionId: ID!) {
movePostToCollection(postId: $postId, collectionId: $collectionId) { id collection { name } }
}

Typical flow

  1. User taps "Save" on a post
  2. SaveToCollectionModal opens as a collection picker (with an option to create a new collection on the spot); the user's default "Saved" collection is one of the choices
  3. The post appears in that collection's collectionPosts results, viewable under ProfilePage.tsx's Saved tab or Settings → Saved
  4. Public collections are browsable via Settings → Discover collections, which links each result to its owner's profile