Used by thousands of students worldwide - help us reach 150 members
Community
/communitySocial feed: posts with image/video media, one-level threaded comments, likes, content reports, user profiles and follows. Every route in this group requires a logged-in user. Authentication failures are answered by the auth middleware with plain-text bodies (401 no token, 403 invalid token or missing role, 404 when the token's user no longer exists).
/community/postsCreate a postGET/community/postsGet the community feedGET/community/posts/:postIdGet a single postPUT/community/posts/:postIdEdit a postDELETE/community/posts/:postIdDelete a postPOST/community/posts/:postId/likeToggle like on a postPOST/community/posts/:postId/commentsComment on a post or reply to a commentGET/community/posts/:postId/commentsList top-level comments on a postPOST/community/comments/:commentId/likeToggle like on a commentGET/community/comments/:commentId/repliesList replies to a commentPUT/community/comments/:commentIdEdit a commentDELETE/community/comments/:commentIdDelete a commentPOST/community/reportsReport a post or commentPOST/community/users/:userId/followToggle follow on a userGET/community/users/:userId/follow-countsGet follower and following countsGET/community/users/:userId/profileGet a user's community profileGET/community/users/:userId/postsList a user's postsCreate a post
Creates a community post, optionally with up to 10 media attachments. Each attachment is uploaded to S3; videos also get a 320x180 JPEG thumbnail generated with ffmpeg (if thumbnail generation fails the post is still created without `thumbnailKey`). All followers of the author are sent a `new_post` notification (fire-and-forget). The response contains the stored media keys but no presigned media URLs — fetch the post or feed to get `url` / `thumbnailUrl`. The author's `profilePicture` is replaced by a 1-hour presigned URL when the author has an uploaded picture (`profilePictureKey`).
Send the body as multipart/form-data.
Body parameters
contentrequiredstring | Post body (rich content as produced by the client editor). |
contentPlainTextstring | Plain-text version of the content (text-indexed on the server).Default: '' |
mediafile[] | Up to 10 files under the field name `media`. Allowed MIME types: image/jpeg, image/png, image/gif, image/webp, video/mp4, video/quicktime. Max 100 MB per file. |
Errors
400`content` is missing401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)500Upload/S3/database failure. A rejected file (disallowed MIME type, file over 100 MB, more than 10 files) is also answered with a plain-text 500 by the global error handler.
curl -X POST "https://app.ummahspot.com/community/posts" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-F "content=value" \
-F "media=@/path/to/file"{
"post": {
"_id": "66f1a2b3c4d5e6f708192a3b",
"author": {
"_id": "64d0f1e2a3b4c5d6e7f80910",
"username": "abdullah",
"profilePicture": "https://bucket.s3.amazonaws.com/profile-pictures/abc.jpg?X-Amz-Signature=...",
"profilePictureKey": "profile-pictures/abc.jpg"
},
"content": "<p>Finished reading the first chapter today.</p>",
"contentPlainText": "Finished reading the first chapter today.",
"media": [
{
"_id": "66f1a2b3c4d5e6f708192a3c",
"s3Key": "community/videos/0b9c1e0e-6a53-4c0b-9a0a-3f1f0f7f2a11-clip.mp4",
"fileName": "clip.mp4",
"fileType": "video/mp4",
"fileSize": 10485760,
"mediaType": "video",
"thumbnailKey": "community/thumbnails/7d2f6c1a-2f0e-4a53-8a61-1c2b3d4e5f60.jpg"
}
],
"likes": [],
"likesCount": 0,
"commentsCount": 0,
"isDeleted": false,
"deletedAt": null,
"deletedBy": null,
"createdAt": "2026-09-18T10:15:00.000Z",
"updatedAt": "2026-09-18T10:15:00.000Z",
"__v": 0
}
}Get the community feed
Returns all non-deleted posts from all users, newest first (the feed is global — it is not limited to followed users). Page-based pagination via `page` and `limit` (no upper bound is enforced on `limit`). Each media item gets a 1-hour presigned `url` (and `thumbnailUrl` for videos). The raw `likes` array is removed and replaced by a `hasLiked` flag for the current user.
Query parameters
pagenumber | 1-based page number.Default: 1 |
limitnumber | Posts per page.Default: 10 |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)500Database error
curl -X GET "https://app.ummahspot.com/community/posts" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"posts": [
{
"_id": "66f1a2b3c4d5e6f708192a3b",
"author": {
"_id": "64d0f1e2a3b4c5d6e7f80910",
"username": "abdullah",
"profilePicture": "https://bucket.s3.amazonaws.com/profile-pictures/abc.jpg?X-Amz-Signature=...",
"profilePictureKey": "profile-pictures/abc.jpg"
},
"content": "<p>Finished reading the first chapter today.</p>",
"contentPlainText": "Finished reading the first chapter today.",
"media": [
{
"_id": "66f1a2b3c4d5e6f708192a3c",
"s3Key": "community/images/0b9c1e0e-6a53-4c0b-9a0a-3f1f0f7f2a11-page.png",
"fileName": "page.png",
"fileType": "image/png",
"fileSize": 482113,
"mediaType": "image",
"url": "https://bucket.s3.amazonaws.com/community/images/0b9c1e0e-...-page.png?X-Amz-Signature=..."
}
],
"likesCount": 3,
"commentsCount": 1,
"isDeleted": false,
"deletedAt": null,
"deletedBy": null,
"createdAt": "2026-09-18T10:15:00.000Z",
"updatedAt": "2026-09-18T10:20:00.000Z",
"__v": 3,
"hasLiked": true
}
],
"currentPage": 1,
"totalPages": 5,
"totalPosts": 42
}Get a single post
Returns one non-deleted post with 1-hour presigned media URLs and a `hasLiked` flag for the current user (the raw `likes` array is removed).
Path parameters
postIdstring | Post ObjectId |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)404Post not found or deleted500Database error (including a malformed `postId`)
curl -X GET "https://app.ummahspot.com/community/posts/:postId" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"post": {
"_id": "66f1a2b3c4d5e6f708192a3b",
"author": {
"_id": "64d0f1e2a3b4c5d6e7f80910",
"username": "abdullah",
"profilePicture": "https://lh3.googleusercontent.com/a/photo.jpg"
},
"content": "<p>Finished reading the first chapter today.</p>",
"contentPlainText": "Finished reading the first chapter today.",
"media": [
{
"_id": "66f1a2b3c4d5e6f708192a3c",
"s3Key": "community/videos/0b9c1e0e-...-clip.mp4",
"fileName": "clip.mp4",
"fileType": "video/mp4",
"fileSize": 10485760,
"mediaType": "video",
"thumbnailKey": "community/thumbnails/7d2f6c1a-....jpg",
"url": "https://bucket.s3.amazonaws.com/community/videos/...?X-Amz-Signature=...",
"thumbnailUrl": "https://bucket.s3.amazonaws.com/community/thumbnails/...?X-Amz-Signature=..."
}
],
"likesCount": 3,
"commentsCount": 1,
"isDeleted": false,
"deletedAt": null,
"deletedBy": null,
"createdAt": "2026-09-18T10:15:00.000Z",
"updatedAt": "2026-09-18T10:20:00.000Z",
"__v": 3,
"hasLiked": false
}
}Edit a post
Updates the text of a post. Only the post author may edit (admins cannot edit other users' posts). Media cannot be changed. An empty `content` is ignored rather than applied. Unlike the read endpoints, the response returns the stored document as-is: it includes the raw `likes` array of user IDs, has no `hasLiked` flag and no presigned media URLs.
Path parameters
postIdstring | Post ObjectId |
Body parameters
contentstring | New post body. Ignored when empty. |
contentPlainTextstring | New plain-text version. Applied whenever the key is present (may be an empty string). |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)403The current user is not the post author404Post not found or deleted500Database error
curl -X PUT "https://app.ummahspot.com/community/posts/:postId" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"content":"string"}'{
"post": {
"_id": "66f1a2b3c4d5e6f708192a3b",
"author": {
"_id": "64d0f1e2a3b4c5d6e7f80910",
"username": "abdullah",
"profilePicture": "https://lh3.googleusercontent.com/a/photo.jpg"
},
"content": "<p>Finished the first two chapters today.</p>",
"contentPlainText": "Finished the first two chapters today.",
"media": [],
"likes": ["64d0f1e2a3b4c5d6e7f80999"],
"likesCount": 1,
"commentsCount": 0,
"isDeleted": false,
"deletedAt": null,
"deletedBy": null,
"createdAt": "2026-09-18T10:15:00.000Z",
"updatedAt": "2026-09-18T11:02:00.000Z",
"__v": 1
}
}Delete a post
Soft-deletes a post (sets `isDeleted`, `deletedAt`, `deletedBy`). Allowed for the post author or any admin. Deleted posts disappear from every read endpoint.
Path parameters
postIdstring | Post ObjectId |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)403The current user is neither the author nor an admin404Post not found or already deleted500Database error
curl -X DELETE "https://app.ummahspot.com/community/posts/:postId" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"message": "Post deleted successfully"
}Toggle like on a post
Likes the post if the current user has not liked it yet, otherwise removes the like. When a like is added, the post author is sent a `like_on_post` notification. No request body.
Path parameters
postIdstring | Post ObjectId |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)404Post not found or deleted500Database error
curl -X POST "https://app.ummahspot.com/community/posts/:postId/like" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"hasLiked": true,
"likesCount": 4
}Comment on a post or reply to a comment
Creates a top-level comment, or a reply when `parentComment` is given. Replies are limited to one level: the parent must be a non-deleted top-level comment on the same post. Recalculates the post's `commentsCount` (and the parent's `repliesCount`). Sends a `comment_on_post` notification to the post author, or a `reply_to_comment` notification to the parent comment's author. Comment content is limited to 2000 characters by the model (longer content fails with a 500).
Path parameters
postIdstring | Post ObjectId |
Body parameters
contentrequiredstring | Comment text, max 2000 characters. |
parentCommentstring | ObjectId of the top-level comment being replied to. Omit for a top-level comment.Default: null |
Errors
400`content` is missing, or `parentComment` is itself a reply (replies can only be one level deep)401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)404Post not found or deleted, or parent comment not found on this post500Database/validation error (e.g. content over 2000 characters)
curl -X POST "https://app.ummahspot.com/community/posts/:postId/comments" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"content":"string"}'{
"comment": {
"_id": "66f1b0c1d2e3f4a5b6c7d8e9",
"post": "66f1a2b3c4d5e6f708192a3b",
"author": {
"_id": "64d0f1e2a3b4c5d6e7f80910",
"username": "abdullah",
"profilePicture": "https://lh3.googleusercontent.com/a/photo.jpg"
},
"parentComment": null,
"content": "JazakAllahu khayran, very helpful.",
"likes": [],
"likesCount": 0,
"repliesCount": 0,
"isDeleted": false,
"deletedAt": null,
"deletedBy": null,
"createdAt": "2026-09-18T10:30:00.000Z",
"updatedAt": "2026-09-18T10:30:00.000Z",
"__v": 0
}
}List top-level comments on a post
Returns the non-deleted top-level comments of a post, oldest first, with page-based pagination. Replies are not included — use `repliesCount` and the replies endpoint. The raw `likes` array is replaced by a `hasLiked` flag. The handler does not check that the post itself exists; an unknown post simply yields an empty list.
Path parameters
postIdstring | Post ObjectId |
Query parameters
pagenumber | 1-based page number.Default: 1 |
limitnumber | Comments per page.Default: 20 |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)500Database error (including a malformed `postId`)
curl -X GET "https://app.ummahspot.com/community/posts/:postId/comments" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"comments": [
{
"_id": "66f1b0c1d2e3f4a5b6c7d8e9",
"post": "66f1a2b3c4d5e6f708192a3b",
"author": {
"_id": "64d0f1e2a3b4c5d6e7f80910",
"username": "abdullah",
"profilePicture": "https://lh3.googleusercontent.com/a/photo.jpg"
},
"parentComment": null,
"content": "JazakAllahu khayran, very helpful.",
"likesCount": 2,
"repliesCount": 1,
"isDeleted": false,
"deletedAt": null,
"deletedBy": null,
"createdAt": "2026-09-18T10:30:00.000Z",
"updatedAt": "2026-09-18T10:45:00.000Z",
"__v": 2,
"hasLiked": false
}
],
"currentPage": 1,
"totalPages": 1,
"totalComments": 1
}Toggle like on a comment
Likes the comment (or reply) if the current user has not liked it yet, otherwise removes the like. When a like is added, the comment author is sent a `like_on_comment` notification. No request body.
Path parameters
commentIdstring | Comment ObjectId |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)404Comment not found or deleted500Database error
curl -X POST "https://app.ummahspot.com/community/comments/:commentId/like" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"hasLiked": false,
"likesCount": 1
}List replies to a comment
Returns the non-deleted replies to a comment, oldest first, with page-based pagination. The raw `likes` array is replaced by a `hasLiked` flag. The handler does not check that the parent comment exists; an unknown ID yields an empty list.
Path parameters
commentIdstring | ObjectId of the parent (top-level) comment |
Query parameters
pagenumber | 1-based page number.Default: 1 |
limitnumber | Replies per page.Default: 20 |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)500Database error (including a malformed `commentId`)
curl -X GET "https://app.ummahspot.com/community/comments/:commentId/replies" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"replies": [
{
"_id": "66f1b4d5e6f7a8b9c0d1e2f3",
"post": "66f1a2b3c4d5e6f708192a3b",
"author": {
"_id": "64d0f1e2a3b4c5d6e7f80999",
"username": "maryam",
"profilePicture": "https://lh3.googleusercontent.com/a/other.jpg"
},
"parentComment": "66f1b0c1d2e3f4a5b6c7d8e9",
"content": "Wa iyyak!",
"likesCount": 0,
"repliesCount": 0,
"isDeleted": false,
"deletedAt": null,
"deletedBy": null,
"createdAt": "2026-09-18T10:45:00.000Z",
"updatedAt": "2026-09-18T10:45:00.000Z",
"__v": 0,
"hasLiked": false
}
],
"currentPage": 1,
"totalPages": 1,
"totalReplies": 1
}Edit a comment
Updates the text of a comment or reply. Only the comment author may edit (admins cannot edit other users' comments). An empty `content` is ignored. The response returns the stored document as-is, including the raw `likes` array of user IDs and no `hasLiked` flag.
Path parameters
commentIdstring | Comment ObjectId |
Body parameters
contentstring | New comment text, max 2000 characters. Ignored when empty. |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)403The current user is not the comment author404Comment not found or deleted500Database/validation error (e.g. content over 2000 characters)
curl -X PUT "https://app.ummahspot.com/community/comments/:commentId" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"content":"string"}'{
"comment": {
"_id": "66f1b0c1d2e3f4a5b6c7d8e9",
"post": "66f1a2b3c4d5e6f708192a3b",
"author": {
"_id": "64d0f1e2a3b4c5d6e7f80910",
"username": "abdullah",
"profilePicture": "https://lh3.googleusercontent.com/a/photo.jpg"
},
"parentComment": null,
"content": "JazakAllahu khayran, this was very helpful.",
"likes": ["64d0f1e2a3b4c5d6e7f80999"],
"likesCount": 1,
"repliesCount": 1,
"isDeleted": false,
"deletedAt": null,
"deletedBy": null,
"createdAt": "2026-09-18T10:30:00.000Z",
"updatedAt": "2026-09-18T11:10:00.000Z",
"__v": 1
}
}Delete a comment
Soft-deletes a comment or reply. Allowed for the comment author or any admin. Recalculates the post's `commentsCount` and, for a reply, the parent's `repliesCount`. Replies to a deleted top-level comment are not themselves deleted.
Path parameters
commentIdstring | Comment ObjectId |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)403The current user is neither the author nor an admin404Comment not found or already deleted500Database error
curl -X DELETE "https://app.ummahspot.com/community/comments/:commentId" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"message": "Comment deleted successfully"
}Report a post or comment
Submits a moderation report against a non-deleted post or comment. A user can report a given target only once (unique on reporter + targetType + targetId). New reports start with status `pending`. `reason` must be one of the model's enum values; any other value fails validation and is answered with a 500.
Body parameters
targetTyperequiredstring | `'post'` or `'comment'`. |
targetIdrequiredstring | ObjectId of the post or comment being reported. |
reasonrequiredstring | One of `'spam'`, `'harassment'`, `'inappropriate_content'`, `'misinformation'`, `'other'`. |
detailsstring | Free-text explanation.Default: '' |
Errors
400`targetType`, `targetId` or `reason` missing, or `targetType` is not `post`/`comment`401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)404Target post or comment not found (or deleted)409The current user has already reported this content500Database/validation error (e.g. `reason` not in the allowed list)
curl -X POST "https://app.ummahspot.com/community/reports" \
-H "Authorization: Bearer $SHARH_TOKEN" \
-H "Content-Type: application/json" \
-d '{"targetType":"string","targetId":"string","reason":"string"}'{
"report": {
"_id": "66f1c7e8f9a0b1c2d3e4f5a6",
"reporter": "64d0f1e2a3b4c5d6e7f80910",
"targetType": "post",
"targetId": "66f1a2b3c4d5e6f708192a3b",
"reason": "spam",
"details": "Repeated advertising links",
"status": "pending",
"reviewedBy": null,
"actionTaken": null,
"reviewedAt": null,
"createdAt": "2026-09-18T11:20:00.000Z",
"updatedAt": "2026-09-18T11:20:00.000Z",
"__v": 0
}
}Toggle follow on a user
Follows the target user if not already followed, otherwise unfollows. Updates `followersCount` on the target and `followingCount` on the current user. No request body.
Path parameters
userIdstring | ObjectId of the user to follow or unfollow |
Errors
400`userId` is the current user (you cannot follow yourself)401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)404Target user not found500Database error
curl -X POST "https://app.ummahspot.com/community/users/:userId/follow" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"isFollowing": true
}Get follower and following counts
Lightweight lookup of a user's follower/following counters. Missing counters are returned as 0.
Path parameters
userIdstring | User ObjectId |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)404User not found500Database error
curl -X GET "https://app.ummahspot.com/community/users/:userId/follow-counts" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"followersCount": 12,
"followingCount": 5
}Get a user's community profile
Returns the public community profile of a user plus whether the current user follows them. `profilePicture` is a 1-hour presigned URL when the user uploaded a picture, otherwise the stored (e.g. Google) picture URL.
Path parameters
userIdstring | User ObjectId |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)404User not found500Database error
curl -X GET "https://app.ummahspot.com/community/users/:userId/profile" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"user": {
"_id": "64d0f1e2a3b4c5d6e7f80999",
"username": "maryam",
"profilePicture": "https://lh3.googleusercontent.com/a/other.jpg",
"followersCount": 12,
"followingCount": 5,
"createdAt": "2025-03-02T08:00:00.000Z",
"isFollowing": true
}
}List a user's posts
Returns the non-deleted posts written by one user, newest first, with page-based pagination. Same item shape as the feed: 1-hour presigned media URLs and a `hasLiked` flag instead of the raw `likes` array. The handler does not check that the user exists; an unknown ID yields an empty list.
Path parameters
userIdstring | ObjectId of the post author |
Query parameters
pagenumber | 1-based page number.Default: 1 |
limitnumber | Posts per page.Default: 10 |
Errors
401No bearer token provided (plain-text body)403Invalid or expired token, or the user lacks the required role (plain-text body)500Database error (including a malformed `userId`)
curl -X GET "https://app.ummahspot.com/community/users/:userId/posts" \
-H "Authorization: Bearer $SHARH_TOKEN"{
"posts": [
{
"_id": "66f1a2b3c4d5e6f708192a3b",
"author": {
"_id": "64d0f1e2a3b4c5d6e7f80999",
"username": "maryam",
"profilePicture": "https://lh3.googleusercontent.com/a/other.jpg"
},
"content": "<p>Notes from today's lesson.</p>",
"contentPlainText": "Notes from today's lesson.",
"media": [],
"likesCount": 0,
"commentsCount": 0,
"isDeleted": false,
"deletedAt": null,
"deletedBy": null,
"createdAt": "2026-09-17T18:00:00.000Z",
"updatedAt": "2026-09-17T18:00:00.000Z",
"__v": 0,
"hasLiked": false
}
],
"currentPage": 1,
"totalPages": 1,
"totalPosts": 1
}