Saved Posts & Collections — Technical Reference
← Back to Saved Posts & Collections
Where this lives
Backend
apps/backend/graphql/resolvers/saved-post.resolver.js— resolvescollectionPosts,isPostSaved,savedPostsCount,savePost,unsavePost,movePostToCollection(the old cross-collectionmySavedPostsquery was removed; posts are now only queried per collection viacollectionPosts)apps/backend/graphql/resolvers/saved-collection.resolver.js— resolvesmyCollections,publicCollections,createCollection,updateCollection,deleteCollection,toggleCollectionPrivacyapps/backend/graphql/types/saved-post.type.js—SavedPostGraphQL schemaapps/backend/graphql/types/saved-collection.type.js—SavedCollectionGraphQL schemaapps/backend/managers/post-managers/saved-post.manager.js— saved-post business logic/validationapps/backend/managers/post-managers/saved-collection.manager.js— collection business logic/validation
Frontend
apps/frontend-nextjs/src/components/PostCard.tsx—handleToggleSavewires the bookmark icon: unsaving is a directunsavePostcall, saving opensSaveToCollectionModalso the post can be dropped into a specific collection; reflectsisSavedstateapps/frontend-nextjs/src/components/SavedCollectionsSection.tsx— renders underProfilePage.tsx's "Saved" tab and at/settings/saved(SavedPage.tsx); queriesmyCollections/collectionPosts, wirescreateCollection/deleteCollection/updateCollection(rename)/toggleCollectionPrivacy/movePostToCollectionapps/frontend-nextjs/src/components/SaveToCollectionModal.tsx— picker for which collection to save a post into, also wirescreateCollectionapps/frontend-nextjs/src/page-components/settings/SavedPage.tsx—Settings → Saved(/settings/saved), just rendersSavedCollectionsSectionapps/frontend-nextjs/src/page-components/settings/DiscoverCollectionsPage.tsx—Settings → Discover collections(/settings/discover-collections); queriespublicCollections, links each result to/{username}
Technical implementation checklist
-
savePost/unsavePost— resolvers wired;PostCard.tsx's bookmark icon is a real working toggle -
myCollections/collectionPosts— wired; consumed bySavedCollectionsSection.tsxunder the Saved tab. There is no more all-collections-at-once query (mySavedPostswas removed); the UI drills into one collection at a time. -
createCollection/deleteCollection— wired; consumed bySavedCollectionsSection.tsx/SaveToCollectionModal.tsx -
updateCollection(rename) — wired;SavedCollectionsSection.tsx'shandleRenamecalls it from the collection detail view -
toggleCollectionPrivacy— wired;SavedCollectionsSection.tsx'shandleTogglePrivacy(Public/Private pill in the collection detail view) -
movePostToCollection— wired;SavedCollectionsSection.tsx'shandleMovePost(hover a post → "Move" → pick target) -
publicCollections— wired;DiscoverCollectionsPage.tsxat/settings/discover-collections
Collections (SavedCollection)
| Field | Description |
|---|---|
name | Collection name |
description | Optional description |
isPublic | Visible to other users |
isDefault | Auto-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 |
postCount | Number 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
- User taps "Save" on a post
SaveToCollectionModalopens 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- The post appears in that collection's
collectionPostsresults, viewable underProfilePage.tsx's Saved tab orSettings → Saved - Public collections are browsable via
Settings → Discover collections, which links each result to its owner's profile